Стандартные коды ошибок:
Ошибка | Значение |
unspecified | Тип ошибки не указан. Подробности смотрите в сообщении. |
invalid_api_key | Указан неправильный ключ доступа к API. Проверьте, совпадает ли значение api_key со значением, указанным в личном кабинете. |
access_denied | Доступ запрещён. Проверьте, включён ли доступ к API в личном кабинете и не обращаетесь ли вы к методу, прав доступа к которому у вас нет. |
unknown_method | Указано неправильное имя метода. |
invalid_arg | Указано неправильное значение одного из аргументов метода. |
not_enough_money | Не хватает денег на счету для выполнения метода. |
retry_later | Временный сбой. Попробуйте ещё раз позднее. |
api_call_limit_exceeded_for_api_key | Сработало ограничение по вызову методов API в единицу времени. На данный момент это 1200 вызовов в минуту. Для метода sendEmail — 60. |
api_call_limit_exceeded_for_ip | Сработало ограничение по вызову методов API в единицу времени. На данный момент это 1200 вызовов в минуту. Для метода sendEmail — 60. |
Частые ошибки без указания типа:
Ошибка | Значение |
«These list ids=»…» has no confirmation letters» | Не создано письмо подтверждение для списка, на который подписывается адресат. Откройте в личном кабинете список контактов на который настроена подписка, внизу слева есть пункт «Инструменты подписки и отписки». Заполните все поля на этой странице и сохраните. Инструкция по созданию письма подтверждения. |
“Contacts test@example.org and +77777777 already exist but owned by different subscribers” | Вы добавляете email и телефон контакта, который уже есть в вашем кабинете. Укажите email или телефон, который не принадлежит другому адресату. |
«Call to a member function getMessage() on boolean» | Вызов осуществляется с одновременным использованием методов GET и POST. Выполняйте запрос только одним из данных методов. |
Кроме этих ошибок, могут быть и другие, указанные в описаниях конкретных методов.
Пример ответа с ошибкой:
{"error":"AK100310-02","code":"invalid_api_key"}
Содержание:
- Общие сведения
- Формат ошибок
- Формат ответа методов API в случае ошибок
- Структура ответа
- Описание параметров
- Формат ответа методов API в случае ошибок
- Описание общих ошибок API
Общие сведения
В документе описан формат ошибок методов API, а также приведен перечень общих ошибок, которые могут возникать при обращении к методам API.
Формат ошибок
Формат ответа методов API в случае ошибок
Структура ответа
- JSON
- XML
{ "metadata":{ "status":400, "detail":"abc", "generated_at":"2015-06-18 12:37:28" }, "errors":[ { "code":281016, "title":"ошибка упрощённой отправки", "detail":"контрагент с минимальным набором данных не может быть отправителем по заказу", "link":"https://dev.dellin.ru/api/ordering/request/#error_281016", "fields":["receiver"] }, { "code":281017, "title":"Недопустимое значение параметра", "detail":"Данный параметр может содержать только значения из списка доступных значений", "link":"https://dev.dellin.ru/api/ordering/request/#error_281017", "fields":["requester"], "validValues":[1, 2, 3] }, { "code":117004, "title":"значение не найдено в справочнике", "detail":"необходимо выбрать значение из соответствующего справочника", "link":"https://dev.dellin.ru/calculation/pickup/#error_117004", "fields":["requester"], "badValues":["0xa77fcf6a449164ed490133777a68bd00"] } ] }
<response> <metadata> <status>400</status> <detail>abc</detail> <generated_at>2015-06-18 12:37:28</generated_at> </metadata> <errors> <code>281016</code> <title>ошибка упрощённой отправки</title> <detail>контрагент с минимальным набором данных не может быть отправителем по заказу</detail> <link>https://dev.dellin.ru/api/ordering/request/#error_281016</link> <fields>receiver</fields> </errors> <errors> <code>281017</code> <title>Недопустимое значение параметра</title> <detail>Данный параметр может содержать только значения из списка доступных значений</detail> <link>https://dev.dellin.ru/api/ordering/request/#error_281017</link> <fields>requester</fields> <validValues>1</validValues> <validValues>2</validValues> <validValues>3</validValues> </errors> <errors> <code>117004</code> <title>значение не найдено в справочнике</title> <detail>необходимо выбрать значение из соответствующего справочника</detail> <link>https://dev.dellin.ru/calculation/pickup/#error_117004</link> <fields>requester</fields> <badValues>0xa77fcf6a449164ed490133777a68bd00</badValues> </errors> </response>
Описание параметров
Response | ||
---|---|---|
Параметр | Тип | Описание |
metadata | object | Информация об оформленной заявке |
metadata.status | integer |
Эмуляция http-кода состояния |
metadata.detail | string | Текстовое описание ответа сервера |
metadata.generated_at | string | Дата и время генерации ответа сервера |
errors | array of Response.Errors | Перечень ошибок |
Response.Errors | ||
---|---|---|
Параметр | Тип | Описание |
code | integer | Номер ошибки |
title | string |
Краткое описание ошибки |
detail | string | Детальное описание ошибки |
link | string | Ссылка на документацию |
fields | array of string | Список параметров в запросе к методу, вызвавших ошибку |
validValues | array of string | Список доступных значений параметра |
badValues | array of string | Список ошибочных значений, переданных в параметре |
Номер ошибки | http-код | Краткое описание ошибки | Детальное описание ошибки |
---|---|---|---|
100001 |
415 | Некорректный content-type | Допустимые значения content-type: application/json (стандарт RFC4627) и text/xml (стандарт RFC3023) |
100002 |
404 | Метод не найден | Проверьте правильность адреса метода |
100003 |
410 | Метод отключен | Запрошенный метод более не доступен |
100004 |
403 | Отсутствует доступ к методу | Доступ к методу предоставляется по требованию. Для получения доступа обратитесь к персональному менеджеру или в техническую поддержку |
100005 |
429 | Количество запросов к превышено | Превышена допустимая частота запросов. Для увеличения лимита обратитесь к персональному менеджеру или в техническую поддержку |
100006 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
101001 |
401 | Требуется авторизация | Не передан API-ключ |
101002 |
401 | Требуется авторизация | Передан недействительный API-ключ |
101003 |
401 | Требуется авторизация | Требуется передать параметр sessionID |
101004 |
401 | Требуется авторизация | Время жизни сессии истекло |
101005 |
401 | Требуется авторизация | Сессия не найдена или создана с другим API-ключом |
101006 |
401 | Требуется авторизация | Неверный логин или пароль |
101007 |
401 | Требуется авторизация | API-ключ заблокирован. Обратитесь в техническую поддержку |
101008 |
401 | Ошибка парсинга | Запрос не соответствует формату json |
101009 |
401 | Ошибка парсинга | Запрос не соответствует формату xml |
110001 |
400 | Неверный формат параметра | Значение, переданное в параметре, не соответствует требуемому формату |
110002 |
400 | Ошибка типизации | Значение, переданное в параметре, имеет некорректный тип |
110003 |
400 | Отсутствует обязательный параметр | Отсутствует обязательный параметр |
110004 |
400 | Не передан ни один из обязательных параметров | В запросе должен присутствовать хотя бы один параметр из совокупности, однако не указано ни одного |
110005 |
400 | Допустима передача только одного из параметров | Указаны взаимоисключающие параметры, только один из которых может присутствовать в запросе |
110006 |
400 | Превышено ограничение на длину списка | Количество элементов в списке превышает максимально допустимое |
110007 |
400 | Объект не существует | Не найден объект с указанным ID. Проверьте правильность переданного значения |
110008 |
400 | Недопустимый набор параметров | Указанные параметры не должны участвовать в запросе |
120001 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomers не отвечает) |
120002 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomers вернул неизвестную ошибку) |
120101 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomersV2 не отвечает) |
120102 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomersV2 вернул неизвестную ошибку) |
120201 |
400 | Ошибка в параметрах запроса |
Переданы неправильные параметры в запрос (Причина: Переданы некорректные данные в getOrdersTracker) |
120301 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис getPaymentsByOrders не отвечает) |
121001 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
121002 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
130001 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
130002 |
400 | Ошибка выполнения запроса | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130003 |
400 | Указан некорректный документ, удостоверяющий личность | Проверьте правильность переданных значений |
130004 |
400 | Не передан ни один из обязательных параметров |
В запросе должен присутствовать хотя бы один параметр из совокупности, однако не указано ни одного |
130005 |
400 | Отсутствует обязательный параметр | Отсутствует обязательный параметр |
130006 |
400 | Значение превышает допустимое | Габариты превышают допустимые размеры |
130007 |
400 | Неверный формат параметра | Значение, переданное в параметре, не соответствует требуемому формату |
130008 |
400 |
Недопустимое значение параметра | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130009 |
400 | Превышено ограничение на длину значения | Превышена максимально допустимая длина значения поля |
130010 |
400 | Отсутствует согласие с тарифами и правилами перевозки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130014 |
400 | Ошибка наложенного платежа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130015 |
400 | Ошибка оформления услуги | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130017 |
400 | Невозможно оформить заявку на указанное время | — |
130021 |
400 | Услуга недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130022 |
400 | Указан некорректный адрес | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130023 |
400 | Выбран недопустимый терминал | Выбран недопустимый терминал |
130024 |
400 | Превышено ограничение на длину списка | Превышено максимальное количество контрагентов в адресной книге (10000). Необходимо удалить часть записей или обратиться в службу поддержки |
150001 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
150002 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
180001 |
400 | Указан некорректный документ, удостоверяющий личность | Проверьте правильность переданных значений |
180002 |
400 | Указан некорректный адрес | Указан некорректный адрес |
180003 |
400 | Выбран недопустимый терминал | Выбранный терминал не может принять груз с указанными ВГХ |
180004 |
400 | Услуга недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180005 |
400 | Значение превышает допустимое | Весогабаритные характеристики груза превышают допустимые для приёма на терминалах города |
180006 |
400 | Ошибка в параметрах запроса | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180007 |
400 | Недопустимое значение параметра | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180008 |
400 | Ошибка упрощенной отправки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180009 |
400 | Ошибка оформления услуги Доставка в день заказа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180010 |
400 | Ошибка оформления услуги Доставка в точное время | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180011 |
400 | Указан некорректный период работы | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180012 |
400 | Выбранная дата недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180013 |
400 | Ошибка параметров оплаты | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180014 |
400 | Ошибка наложенного платежа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180015 |
400 | Ошибка оформления услуги | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180016 |
400 | Ошибка при сохранении заявки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180017 |
400 | Невозможно оформить заявку на указанное время | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
Error codes are almost the last thing that you want to see in an API response. Generally speaking, it means one of two things — something was so wrong in your request or your handling that the API simply couldn’t parse the passed data, or the API itself has so many problems that even the most well-formed request is going to fail. In either situation, traffic comes crashing to a halt, and the process of discovering the cause and solution begins.
That being said, errors, whether in code form or simple error response, are a bit like getting a shot — unpleasant, but incredibly useful. Error codes are probably the most useful diagnostic element in the API space, and this is surprising, given how little attention we often pay them.
Today, we’re going to talk about exactly why error responses and handling approaches are so useful and important. We’ll take a look at some common error code classifications the average user will encounter, as well as some examples of these codes in action. We’ll also talk a bit about what makes a “good” error code and what makes a “bad” error code, and how to ensure your error codes are up to snuff.
The Value of Error Codes
As we’ve already said, error codes are extremely useful. Error codes in the response stage of an API is the fundamental way in which a developer can communicate failure to a user. This stage, sitting after the initial request stage, is a direct communication between client and API. It’s often the first and most important step towards not only notifying the user of a failure, but jump-starting the error resolution process.
A user doesn’t choose when an error is generated, or what error it gets — error situations often arise in instances that, to the user, are entirely random and suspect. Error responses thus are the only truly constant, consistent communication the user can depend on when an error has occurred. Error codes have an implied value in the way that they both clarify the situation, and communicate the intended functionality.
Consider for instance an error code such as “401 Unauthorized – Please Pass Token.” In such a response, you understand the point of failure, specifically that the user is unauthorized. Additionally, however, you discover the intended functionality — the API requires a token, and that token must be passed as part of the request in order to gain authorization.
With a simple error code and resolution explanation, you’ve not only communicated the cause of the error, but the intended functionality and method to fix said error — that’s incredibly valuable, especially for the amount of data that is actually returned.
HTTP Status Codes
Before we dive deeper into error codes and what makes a “good” code “good,” we need to address the HTTP Status Codes format. These codes are the most common status codes that the average user will encounter, not just in terms of APIs but in terms of general internet usage. While other protocols exist and have their own system of codes, the HTTP Status Codes dominate API communication, and vendor-specific codes are likely to be derived from these ranges.
1XX – Informational
The 1XX range has two basic functionalities. The first is in the transfer of information pertaining to the protocol state of the connected devices — for instance, 101 Switching Protocols
is a status code that notes the client has requested a protocol change from the server, and that the request has been approved. The 1XX range also clarifies the state of the initial request. 100 Continue
, for instance, notes that a server has received request headers from a client, and that the server is awaiting the request body.
2XX – Success
The 2XX range notes a range of successes in communication, and packages several responses into specific codes. The first three status codes perfectly demonstrate this range – 200 OK
means that a GET or POST request was successful, 201 Created
confirms that a request has been fulfilled and a new resource has been created for the client, and 202 Accepted
means that the request has been accepted, and that processing has begun.
3XX – Redirection
The 3XX range is all about the status of the resource or endpoint. When this type of status code is sent, it means that the server is still accepting communication, but that the point contacted is not the correct point of entry into the system. 301 Moved Permanently
verifies that the client request did in fact reach the correct system, but that this request and all future requests should be handled by a different URI. This is very useful in subdomains and when moving a resource from one server to another.
4XX – Client Error
The 4XX series of error codes is perhaps the most famous due to the iconic 404 Not Found
status, which is a well-known marker for URLs and URIs that are incorrectly formed. Other more useful status codes for APIs exist in this range, however.
414 URI Too Long
is a common status code, denoting that the data pushed through in a GET request is too long, and should be converted to a POST request. Another common code is 429 Too many Requests
, which is used for rate limiting to note a client is attempting too many requests at once, and that their traffic is being rejected.
5XX – Server Error
Finally the 5XX range is reserved for error codes specifically related to the server functionality. Whereas the 4XX range is the client’s responsibility (and thus denotes a client failure), the 5XX range specifically notes failures with the server. Error codes like 502 Bad Gateway
, which notes the upstream server has failed and that the current server is a gateway, further expose server functionality as a means of showing where failure is occurring. There are less specific, general failures as well, such as 503 Service Unavailable
.
Making a Good Error Code
With a solid understanding of HTTP Status Codes, we can start to dissect what actually makes for a good error code, and what makes for a bad error code. Quality error codes not only communicate what went wrong, but why it went wrong.
Overly opaque error codes are extremely unhelpful. Let’s imagine that you are attempting to make a GET request to an API that handles digital music inventory. You’ve submitted your request to an API that you know routinely accepts your traffic, you’ve passed the correct authorization and authentication credentials, and to the best of your knowledge, the server is ready to respond.
You send your data, and receive the following error code – 400 Bad Request
. With no additional data, no further information, what does this actually tell you? It’s in the 4XX range, so you know the problem was on the client side, but it does absolutely nothing to communicate the issue itself other than “bad request.”
This is when a “functional” error code is really not as functional as it should be. That same response could easily be made helpful and transparent with minimal effort — but what would this entail? Good error codes must pass three basic criteria in order to truly be helpful. A quality error code should include:
- An HTTP Status Code, so that the source and realm of the problem can be ascertained with ease;
- An Internal Reference ID for documentation-specific notation of errors. In some cases, this can replace the HTTP Status Code, as long as the internal reference sheet includes the HTTP Status Code scheme or similar reference material.
- Human readable messages that summarize the context, cause, and general solution for the error at hand.
Include Standardized Status Codes
First and foremost, every single error code generated should have an attached status code. While this often takes the form of an internal code, it typically takes the form of a standardized status code in the HTTP Status Code scheme. By noting the status using this very specific standardization, you not only communicate the type of error, you communicate where that error has occurred.
There are certain implications for each of the HTTP Status Code ranges, and these implications give a sense as to the responsibility for said error. 5XX errors, for instance, note that the error is generated from the server, and that the fix is necessarily something to do with server-related data, addressing, etc. 4XX, conversely, notes the problem is with the client, and specifically the request from the client or the status of the client at that moment.
By addressing error codes using a default status, you can give a very useful starting point for even basic users to troubleshoot their errors.
Give Context
First and foremost, an error code must give context. In our example above, 400 Bad Request
means nothing. Instead, an error code should give further context. One such way of doing this is by passing this information in the body of the response in the language that is common to the request itself.
For instance, our error code of 400 Bad Request
can easily have a JSON body that gives far more useful information to the client:
< HTTP/1.1 400 Bad Request
< Date: Wed, 31 May 2017 19:01:41 GMT
< Server: Apache/2.4.25 (Ubuntu)
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: application/json
{ "error" : "REQUEST - BR0x0071" }
This error code is good, but not great. What does it get right? Well, it supplies context, for starters. Being able to see what the specific type of failure is shows where the user can begin the problem solving process. Additionally, and vitally, it also gives an internal reference ID in the form of “BR0x0071”, which can be internally referenced.
While this is an ok error code, it only meets a fraction of our requirements.
Human Readability
Part of what makes error codes like the one we just created so powerful is that it’s usable by humans and machines alike. Unfortunately, this is a very easy thing to mess up — error codes are typically handled by machines, and so it’s very tempting to simply code for the application rather than for the user of said application.
In our newly formed example, we have a very clear error to handle, but we have an additional issue. While we’ve added context, that context is in the form of machine-readable reference code to an internal error note. The user would have to find the documentation, look up the request code “BRx0071”, and then figure out what went wrong.
We’ve fallen into that trap of coding for the machine. While our code is succinct and is serviceable insomuch as it provides context, it does so at the cost of human readability. With a few tweaks, we could improve the code, while still providing the reference number as we did before:
< HTTP/1.1 400 Bad Request
< Date: Wed, 31 May 2017 19:01:41 GMT
< Server: Apache/2.4.25 (Ubuntu)
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: application/json
{ "error" : "Bad Request - Your request is missing parameters. Please verify and resubmit. Issue Reference Number BR0x0071" }
With such a response, not only do you get the status code, you also get useful, actionable information. In this case, it tells the user the issue lies within their parameters. This at least offers a place to start troubleshooting, and is far more useful than saying “there’s a problem.”
While you still want to provide the issue reference number, especially if you intend on integrating an issue tracker into your development cycle, the actual error itself is much more powerful, and much more effective than simply shooting a bunch of data at the application user and hoping something sticks.
Good Error Examples
Let’s take a look at some awesome error code implementations on some popular systems.
Twitter API is a great example of descriptive error reporting codes. Let’s attempt to send a GET request to retrieve our mentions timeline.
https://api.twitter.com/1.1/statuses/mentions_timeline.json
When this is sent to the Twitter API, we receive the following response:
HTTP/1.1 400 Bad Request
x-connection-hash:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
set-cookie:
guest_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Date:
Thu, 01 Jun 2017 03:04:23 GMT
Content-Length:
62
x-response-time:
5
strict-transport-security:
max-age=631138519
Connection:
keep-alive
Content-Type:
application/json; charset=utf-8
Server:
tsa_b
{"errors":[{"code":215,"message":"Bad Authentication data."}]}
Looking at this data, we can generally figure out what our issue is. First, we’re told that we’ve submitted a 400 Bad Request
. This tells us that the problem is somewhere in our request. Our content length is acceptable, and our response time is well within normal limits. We can see, however, that we’re receiving a unique error code that Twitter itself has denoted — “215”, with an attached message that states “Bad Authentication data”.
This error code supplies both valuable information as to why the error has occurred, and also how to rectify it. Our error lies in the fact that we did not pass any authentication data whatsoever — accordingly, error 215 is referenced, which tells us the fix is to supply said authentication data, but also gives us a number to reference on the internal documentation of the Twitter API.
For another great example, let’s look at another social network. Facebook’s Graph API allows us to do quite a lot as long as we have the proper authentication data. For the purposes of this article, all personal information will be blanked out for security purposes.
First, let’s pass a GET request to ascertain some details about a user:
https://graph.facebook.com/v2.9/me?fields=id%2Cname%2Cpicture%2C%20picture&access_token=xxxxxxxxxxx
This request should give us a few basic fields from this user’s Facebook profile, including id, name, and picture. Instead, we get this error response:
{
"error": {
"message": "Syntax error "Field picture specified more than once. This is only possible before version 2.1" at character 23: id,name,picture,picture",
"type": "OAuthException",
"code": 2500,
"fbtrace_id": "xxxxxxxxxxx"
}
}
While Facebook doesn’t directly pass the HTTP error code in the body, it does pass a lot of useful information. The “message” area notes that we’ve run into a syntax error, specifically that we’ve defined the “picture” field more than once. Additionally, this field lets us know that this behavior was possible in previous versions, which is a very useful tool to communicate to users a change in behavior from previous versions to the current.
Additionally, we are provided both a code and an fbtrace_id
that can be used with support to identify specific issues in more complex cases. We’ve also received a specific error type, in this case OAuthException
, which can be used to narrow down the specifics of the case even further.
Bing
To show a complex failure response code, let’s send a poorly formed (essentially null) GET request to Bing.
HTTP/1.1 200
Date:
Thu, 01 Jun 2017 03:40:55 GMT
Content-Length:
276
Connection:
keep-alive
Content-Type:
application/json; charset=utf-8
Server:
Microsoft-IIS/10.0
X-Content-Type-Options:
nosniff
{"SearchResponse":{"Version":"2.2","Query":{"SearchTerms":"api error codes"},"Errors":[{"Code":1001,"Message":"Required parameter is missing.","Parameter":"SearchRequest.AppId","HelpUrl":"httpu003au002fu002fmsdn.microsoft.comu002fen-usu002flibraryu002fdd251042.aspx"}]}}
This is a very good error code, perhaps the best of the three we’ve demonstrated here. While we have the error code in the form of “1001”, we also have a message stating that a parameter is missing. This parameter is then specifically noted as “SearchRequestAppId”, and a “HelpUrl” variable is passed as a link to a solution.
In this case, we’ve got the best of all worlds. We have a machine readable error code, a human readable summary, and a direct explanation of both the error itself and where to find more information about the error.
Spotify
Though 5XX errors are somewhat rare in modern production environments, we do have some examples in bug reporting systems. One such report noted a 5XX error generated from the following call:
GET /v1/me/player/currently-playing
This resulted in the following error:
[2017-05-02 13:32:14] production.ERROR: GuzzleHttpExceptionServerException: Server error: `GET https://api.spotify.com/v1/me/player/currently-playing` resulted in a `502 Bad Gateway` response:
{
"error" : {
"status" : 502,
"message" : "Bad gateway."
}
}
So what makes this a good error code? While the 502 Bad gateway error seems opaque, the additional data in the header response is where our value is derived. By noting the error occurring in production and its addressed variable, we get a general sense that the issue at hand is one of the server gateway handling an exception rather than anything external to the server. In other words, we know the request entered the system, but was rejected for an internal issue at that specific exception address.
When addressing this issue, it was noted that 502 errors are not abnormal, suggesting this to be an issue with server load or gateway timeouts. In such a case, it’s almost impossible to note granularly all of the possible variables — given that situation, this error code is about the best you could possibly ask for.
Conclusion
Much of an error code structure is stylistic. How you reference links, what error code you generate, and how to display those codes is subject to change from company to company. However, there has been headway to standardize these approaches; the IETF recently published RFC 7807, which outlines how to use a JSON object as way to model problem details within HTTP response. The idea is that by providing more specific machine-readable messages with an error response, the API clients can react to errors more effectively.
In general, the goal with error responses is to create a source of information to not only inform the user of a problem, but of the solution to that problem as well. Simply stating a problem does nothing to fix it – and the same is true of API failures.
The balance then is one of usability and brevity. Being able to fully describe the issue at hand and present a usable solution needs to be balanced with ease of readability and parsability. When that perfect balance is struck, something truly powerful happens.
While it might seem strange to wax philosophically about error codes, they are a truly powerful tool that go largely underutilized. Incorporating them in your code is not just a good thing for business and API developer experience – they can lead to more positive end user experience, driving continuous adoption and usage.
Some Background
REST APIs use the Status-Line part of an HTTP response message to inform clients of their request’s overarching result.
RFC 2616 defines the Status-Line syntax as shown below:
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
A great amount of applications are using Restful APIs that are based on the HTTP protocol for connecting their clients. In all the calls, the server and the endpoint at the client both return a call status to the client which can be in the form of:
- The success of API call.
- Failure of API call.
In both the cases, it is necessary to let the client know so that they can proceed to the next step. In the case of a successful API call they can proceed to the next call or whatever their intent was in the first place but in the case of latter they will be forced to modify their call so that the failed call can be recovered.
RestCase
To enable the best user experience for your customer, it is necessary on the part of the developers to make excellent error messages that can help their client to know what they want to do with the information they get. An excellent error message is precise and lets the user know about the nature of the error so that they can figure their way out of it.
A good error message also allows the developers to get their way out of the failed call.
Next step is to know what error messages to integrate into your framework so that the clients on the end point and the developers at the server are constantly made aware of the situation which they are in. in order to do so, the rule of thumb is to keep the error messages to a minimum and only incorporate those error messages which are helpful.
HTTP defines over 40 standard status codes that can be used to convey the results of a client’s request. The status codes are divided into the five categories presented here:
- 1xx: Informational — Communicates transfer protocol-level information
- 2xx: Success -Indicates that the client’s request was accepted successfully.
- 3xx: Redirection — Indicates that the client must take some additional action in order to complete their request.
- 4xx: Client Error — This category of error status codes points the finger at clients.
- 5xx: Server Error — The server takes responsibility for these error status codes.
If you would ask me 5 years ago about HTTP Status codes I would guess that the talk is about web sites, status 404 meaning that some page was not found and etc. But today when someone asks me about HTTP Status codes, it is 99.9% refers to REST API web services development. I have lots of experience in both areas (Website development, REST API web services development) and it is sometimes hard to come to a conclusion about what and how use the errors in REST APIs.
There are some cases where this status code is always returned, even if there was an error that occurred. Some believe that returning status codes other than 200 is not good as the client did reach your REST API and got response.
Proper use of the status codes will help with your REST API management and REST API workflow management.
If for example the user asked for “account” and that account was not found there are 2 options to use for returning an error to the user:
-
Return 200 OK Status and in the body return a json containing explanation that the account was not found.
-
Return 404 not found status.
The first solution opens up a question whether the user should work a bit harder to parse the json received and to see whether that json contains error or not. -
There is also a third solution: Return 400 Error — Client Error. I will explain a bit later why this is my favorite solution.
It is understandable that for the user it is easier to check the status code of 404 without any parsing work to do.
I my opinion this solution is actually miss-use of the HTTP protocol
We did reach the REST API, we did got response from the REST API, what happens if the users misspells the URL of the REST API – he will get the 404 status but that is returned not by the REST API itself.
I think that these solutions should be interesting to explore and to see the benefits of one versus the other.
There is also one more solution that is basically my favorite – this one is a combination of the first two solutions, he is also gives better Restful API services automatic testing support because only several status codes are returned, I will try to explain about it.
Error handling Overview
Error responses should include a common HTTP status code, message for the developer, message for the end-user (when appropriate), internal error code (corresponding to some specific internally determined ID), links where developers can find more info. For example:
‘{ «status» : 400,
«developerMessage» : «Verbose, plain language description of the problem. Provide developers suggestions about how to solve their problems here»,
«userMessage» : «This is a message that can be passed along to end-users, if needed.»,
«errorCode» : «444444»,
«moreInfo» : «http://www.example.gov/developer/path/to/help/for/444444,
http://tests.org/node/444444»,
}’
How to think about errors in a pragmatic way with REST?
Apigee’s blog post that talks about this issue compares 3 top API providers.
No matter what happens on a Facebook request, you get back the 200 status code — everything is OK. Many error messages also push down into the HTTP response. Here they also throw an #803 error but with no information about what #803 is or how to react to it.
Twilio
Twilio does a great job aligning errors with HTTP status codes. Like Facebook, they provide a more granular error message but with a link that takes you to the documentation. Community commenting and discussion on the documentation helps to build a body of information and adds context for developers experiencing these errors.
SimpleGeo
Provides error codes but with no additional value in the payload.
Error Handling — Best Practises
First of all: Use HTTP status codes! but don’t overuse them.
Use HTTP status codes and try to map them cleanly to relevant standard-based codes.
There are over 70 HTTP status codes. However, most developers don’t have all 70 memorized. So if you choose status codes that are not very common you will force application developers away from building their apps and over to wikipedia to figure out what you’re trying to tell them.
Therefore, most API providers use a small subset.
For example, the Google GData API uses only 10 status codes, Netflix uses 9, and Digg, only 8.
How many status codes should you use for your API?
When you boil it down, there are really only 3 outcomes in the interaction between an app and an API:
- Everything worked
- The application did something wrong
- The API did something wrong
Start by using the following 3 codes. If you need more, add them. But you shouldn’t go beyond 8.
- 200 — OK
- 400 — Bad Request
- 500 — Internal Server Error
Please keep in mind the following rules when using these status codes:
200 (OK) must not be used to communicate errors in the response body
Always make proper use of the HTTP response status codes as specified by the rules in this section. In particular, a REST API must not be compromised in an effort to accommodate less sophisticated HTTP clients.
400 (Bad Request) may be used to indicate nonspecific failure
400 is the generic client-side error status, used when no other 4xx error code is appropriate. For errors in the 4xx category, the response body may contain a document describing the client’s error (unless the request method was HEAD).
500 (Internal Server Error) should be used to indicate API malfunction 500 is the generic REST API error response.
Most web frameworks automatically respond with this response status code whenever they execute some request handler code that raises an exception. A 500 error is never the client’s fault and therefore it is reasonable for the client to retry the exact same request that triggered this response, and hope to get a different response.
If you’re not comfortable reducing all your error conditions to these 3, try adding some more but do not go beyond 8:
- 401 — Unauthorized
- 403 — Forbidden
- 404 — Not Found
Please keep in mind the following rules when using these status codes:
A 401 error response indicates that the client tried to operate on a protected resource without providing the proper authorization. It may have provided the wrong credentials or none at all.
403 (Forbidden) should be used to forbid access regardless of authorization state
A 403 error response indicates that the client’s request is formed correctly, but the REST API refuses to honor it. A 403 response is not a case of insufficient client credentials; that would be 401 (“Unauthorized”). REST APIs use 403 to enforce application-level permissions. For example, a client may be authorized to interact with some, but not all of a REST API’s resources. If the client attempts a resource interaction that is outside of its permitted scope, the REST API should respond with 403.
404 (Not Found) must be used when a client’s URI cannot be mapped to a resource
The 404 error status code indicates that the REST API can’t map the client’s URI to a resource.
RestCase
Conclusion
I believe that the best solution to handle errors in a REST API web services is the third option, in short:
Use three simple, common response codes indicating (1) success, (2) failure due to client-side problem, (3) failure due to server-side problem:
- 200 — OK
- 400 — Bad Request (Client Error) — A json with error more details should return to the client.
- 401 — Unauthorized
- 500 — Internal Server Error — A json with an error should return to the client only when there is no security risk by doing that.
I think that this solution can also ease the client to handle only these 4 status codes and when getting either 400 or 500 code he should take the response message and parse it in order to see what is the problem exactly and on the other hand the REST API service is simple enough.
The decision of choosing which error messages to incorporate and which to leave is based on sheer insight and intuition. For example: if an app and API only has three outcomes which are; everything worked, the application did not work properly and API did not respond properly then you are only concerned with three error codes. By putting in unnecessary codes, you will only distract the users and force them to consult Google, Wikipedia and other websites.
Most important thing in the case of an error code is that it should be descriptive and it should offer two outputs:
- A plain descriptive sentence explaining the situation in the most precise manner.
- An ‘if-then’ situation where the user knows what to do with the error message once it is returned in an API call.
The error message returned in the result of the API call should be very descriptive and verbal. A code is preferred by the client who is well versed in the programming and web language but in the case of most clients they find it hard to get the code.
As I stated before, 404 is a bit problematic status when talking about Restful APIs. Does this status means that the resource was not found? or that there is not mapping to the requested resource? Everyone can decide what to use and where
Почти все разработчики так или иначе постоянно работают с api по http, клиентские разработчики работают с api backend своего сайта или приложения, а бэкендеры «дергают» бэкенды других сервисов, как внутренних, так и внешних. И мне кажется, одна из самых главных вещей в хорошем API это формат передачи ошибок. Ведь если это сделано плохо/неудобно, то разработчик, использующий это API, скорее всего не обработает ошибки, а клиенты будут пользоваться молчаливо ломающимся продуктом.
За 7 лет я как поддерживал множество legacy API, так и разрабатывал c нуля. И я поработал, наверное, с большинством стратегий по возвращению ошибок, но каждая из них создавала дискомфорт в той или иной мере. В последнее время я нащупал оптимальный вариант, о котором и хочу рассказать, но с начала расскажу о двух наиболее популярных вариантах.
№1: HTTP статусы
Если почитать апологетов REST, то для кодов ошибок надо использовать HTTP статусы, а текст ошибки отдавать в теле или в специальном заголовке. Например:
Success:
HTTP 200 GET /v1/user/1
Body: { name: 'Вася' }
Error:
HTTP 404 GET /v1/user/1
Body: 'Не найден пользователь'
Если у вас примитивная бизнес-логика или API из 5 url, то в принципе это нормальный подход. Однако как-только бизнес-логика станет сложнее, то начнется ряд проблем.
Http статусы предназначались для описания ошибок при передаче данных, а про логику вашего приложения никто не думал. Статусов явно не хватает для описания всего разнообразия ошибок в вашем проекте, да они и не были для этого предназначены. И тут начинается натягивание «совы на глобус»: все начинают спорить, какой статус ошибки дать в том или ином случае. Пример: Есть API для task manager. Какой статус надо вернуть в случае, если пользователь хочет взять задачу, а ее уже взял в работу другой пользователь? Ссылка на http статусы. И таких проблемных примеров можно придумать много.
REST скорее концепция, чем формат общения из чего следует неоднозначность использования статусов. Разработчики используют статусы как им заблагорассудится. Например, некоторые API при отсутствии сущности возвращают 404 и текст ошибки, а некоторые 200 и пустое тело.
Бэкенд разработчику в проекте непросто выбрать статус для ошибки, а клиентскому разработчику неочевидно какой статус предназначен для того или иного типа ошибок бизнес-логики. По-хорошему в проекте придется держать enum для того, чтобы описать какие ошибки относятся к тому или иному статусу.
Когда бизнес-логика приложения усложняется, начинают делать как-то так:
HTTP 400 PUT /v1/task/1 { status: 'doing' }
Body: { error_code: '12', error_message: 'Задача уже взята другим исполнителем' }
Из-за ограниченности http статусов разработчики начинают вводить “свои” коды ошибок для каждого статуса и передавать их в теле ответа. Другими словами, пользователю API приходится писать нечто подобное:
if (status === 200) {
// Success
} else if (status === 500) {
// some code
} else if (status === 400) {
if (body.error_code === 1) {
// some code
} else if (body.error_code === 2) {
// some code
} else {
// some code
}
} else if (status === 404) {
// some code
} else {
// some code
}
Из-за этого ветвление клиентского кода начинает стремительно расти: множество http статусов и множество кодов в самом сообщении. Для каждого ошибочного http статуса необходимо проверить наличие кодов ошибок в теле сообщения. От комбинаторного взрыва начинает конкретно пухнуть башка! А значит обработку ошибок скорее всего сведут к сообщению типа “Произошла ошибка” или к молчаливому некорректному поведению.
Многие системы мониторинга сервисов привязываются к http статусам, но это не помогает в мониторинге, если статусы используются для описания ошибок бизнес логики. Например, у нас резкий всплеск ошибок 429 на графике. Это началась DDOS атака, или кто-то из разработчиков выбрал неудачный статус?
Итог: Начать с таким подходом легко и просто и для простого API это вполне подойдет. Но если логика стала сложнее, то использование статусов для описания того, что не укладывается в заданные рамки протокола http приводит к неоднозначности использования и последующим костылям для работы с ошибками. Или что еще хуже к формализму, что ведет к неприятному пользовательскому опыту.
№2: На все 200
Есть другой подход, даже более старый, чем REST, а именно: на все ошибки связанные с бизнес-логикой возвращать 200, а уже в теле ответа есть информация об ошибке. Например:
Вариант 1:
Success:
HTTP 200 GET /v1/user/1
Body: { ok: true, data: { name: 'Вася' } }
Error:
HTTP 200 GET /v1/user/1
Body: { ok: false, error: { code: 1, msg: 'Не найден пользователь' } }
Вариант 2:
Success:
HTTP 200 GET /v1/user/1
Body: { data: { name: 'Вася' }, error: null }
Error:
HTTP 200 GET /v1/user/1
Body: { data: null, error: { code: 1, msg: 'Не найден пользователь' } }
На самом деле формат зависит от вас или от выбранной библиотеки для реализации коммуникации, например JSON-API.
Звучит здорово, мы теперь отвязались от http статусов и можем спокойно ввести свои коды ошибок. У нас больше нет проблемы “впихнуть невпихуемое”. Выбор нового типа ошибки не вызывает споров, а сводится просто к введению нового числового номера (например, последовательно) или строковой константы. Например:
module.exports = {
NOT_FOUND: 1,
VALIDATION: 2,
// ….
}
module.exports = {
NOT_FOUND: ‘NOT_AUTHORIZED’,
VALIDATION: ‘VALIDATION’,
// ….
}
Клиентские разработчики просто основываясь на кодах ошибок могут создать классы/типы ошибок и притом не бояться, что сервер вернет один и тот же код для разных типов ошибок (из-за бедности http статусов).
Обработка ошибок становится менее ветвящейся, множество http статусов превратились в два: 200 и все остальные (ошибки транспорта).
if (status === 200) {
if (body.error) {
var error = body.error;
if (error.code === 1) {
// some code
} else if (error.code === 2) {
// some code
} else {
// some code
}
} else {
// Success
}
} else {
// transport erros
}
В некоторых случаях, если есть библиотека десериализации данных, она может взять часть работы на себя. Писать SDK вокруг такого подхода проще нежели вокруг той или иной имплементации REST, ведь реализация зависит от того, как это видел автор. Кроме того, теперь никто не вызовет случайное срабатывание alert в мониторинге из-за того, что выбрал неудачный код ошибки.
Но неудобства тоже есть:
-
Избыточность полей при передаче данных, т.е. нужно всегда передавать 2 поля: для данных и для ошибки. Это усложняет чтение логов и написание документации.
-
При использовании средств отладки (Chrome DevTools) или других подобных инструментов вы не сможете быстро найти ошибочные запросы бизнес логики, придется обязательно заглянуть в тело ответа (ведь всегда 200)
-
Мониторинг теперь точно будет срабатывать только на ошибки транспорта, а не бизнес-логики, но для мониторинга логики надо будет дописывать парсинг тела сообщения.
В некоторых случаях данный подход вырождается в RPC, то есть по сути вообще отказываются от использования url и шлют все на один url методом POST, а в теле сообщения передают все параметры. Мне кажется это не правильным, ведь url это прекрасный именованный namespace, зачем от этого отказываться, не понятно?! Кроме того, RPC создает проблемы:
-
нельзя кэшировать по http GET запросы, так как замешали чтение и запись в один метод POST
-
нельзя делать повторы для неудавшихся GET запросов (на backend) на реверс-прокси (например, nginx) по указанной выше причине
-
имеются проблемы с документированием – swagger и ApiDoc не подходят, а удобных аналогов я не нашел
Итог: Для сложной бизнес-логики с большим количеством типов ошибок такой подход лучше, чем расплывчатый REST, не зря в проектах c “разухабистой” бизнес-логикой часто именно такой подход и используют.
№3: Смешанный
Возьмем лучшее от двух миров. Мы выберем один http статус, например, 400 или 422 для всех ошибок бизнес-логики, а в теле ответа будем указывать код ошибки или строковую константу. Например:
Success:
HTTP 200 /v1/user/1
Body: { name: 'Вася' }
Error:
HTTP 400 /v1/user/1
Body: { error: { code: 1, msg: 'Не найден пользователь' } }
Коды:
-
200 – успех
-
400 – ошибка бизнес логики
-
остальное ошибки в транспорте
Тело ответа для удачного запроса у нас имеет произвольную структуру, а вот для ошибки есть четкая схема. Мы избавляемся от избыточности данных (поле ошибки/данных) благодаря использованию http статуса в сравнении со вторым вариантом. Клиентский код упрощается в плане обработки ошибки (в сравнении с первым вариантом). Также мы снижаем его вложенность за счет использования отдельного http статуса для ошибок бизнес логики (в сравнении со вторым вариантом).
if (status === 200) {
// Success
} else if (status === 400) {
if (body.error.code === 1) {
// some code
} else if (body.error.code === 2) {
// some code
} else {
// some code
}
} else {
// transport erros
}
Мы можем расширять объект ошибки для детализации проблемы, если хотим. С мониторингом все как во втором варианте, дописывать парсинг придется, но и риска “стрельбы” некорректными alert нету. Для документирования можем спокойно использовать Swagger и ApiDoc. При этом сохраняется удобство использования инструментов разработчика, таких как Chrome DevTools, Postman, Talend API.
Итог: Использую данный подход уже в нескольких проектах, где множество типов ошибок и все крайне довольны, как клиентские разработчики, так и бэкендеры. Внедрение новой ошибки не вызывает споров, проблем и противоречий. Данный подход объединяет преимущества первого и второго варианта, при этом код более читабельный и структурированный.
Самое главное какой бы формат ошибок вы бы не выбрали лучше обговорить его заранее и следовать ему. Если эту вещь пустить на “самотек”, то очень скоро обработка ошибок в проекте станет невыносимо сложной для всех.
P.S. Иногда ошибки любят передавать массивом
{ error: [{ code: 1, msg: 'Не найден пользователь' }] }
Но это актуально в основном в двух случаях:
-
Когда наш API выступает в роли сервиса без фронтенда (нет сайта/приложения). Например, сервис платежей.
-
Когда в API есть url для загрузки какого-нибудь длинного отчета в котором может быть ошибка в каждой строке/колонке. И тогда для пользователя удобнее, чтобы ошибки в приложении сразу показывались все, а не по одной.
В противном случае нет особого смысла закладываться сразу на массив ошибок, потому что базовая валидация данных должна происходить на клиенте, зато код упрощается как на сервере, так и на клиенте. А user-experience хакеров, лезущих напрямую в наше API, не должен нас волновать?HTTP
Стандартные коды ошибок:
Ошибка | Значение |
unspecified | Тип ошибки не указан. Подробности смотрите в сообщении. |
invalid_api_key | Указан неправильный ключ доступа к API. Проверьте, совпадает ли значение api_key со значением, указанным в личном кабинете. |
access_denied | Доступ запрещён. Проверьте, включён ли доступ к API в личном кабинете и не обращаетесь ли вы к методу, прав доступа к которому у вас нет. |
unknown_method | Указано неправильное имя метода. |
invalid_arg | Указано неправильное значение одного из аргументов метода. |
not_enough_money | Не хватает денег на счету для выполнения метода. |
retry_later | Временный сбой. Попробуйте ещё раз позднее. |
api_call_limit_exceeded_for_api_key | Сработало ограничение по вызову методов API в единицу времени. На данный момент это 1200 вызовов в минуту. Для метода sendEmail — 60. |
api_call_limit_exceeded_for_ip | Сработало ограничение по вызову методов API в единицу времени. На данный момент это 1200 вызовов в минуту. Для метода sendEmail — 60. |
Частые ошибки без указания типа:
Ошибка | Значение |
«These list ids=»…» has no confirmation letters» | Не создано письмо подтверждение для списка, на который подписывается адресат. Откройте в личном кабинете список контактов на который настроена подписка, внизу слева есть пункт «Инструменты подписки и отписки». Заполните все поля на этой странице и сохраните. Инструкция по созданию письма подтверждения. |
“Contacts test@example.org and +77777777 already exist but owned by different subscribers” | Вы добавляете email и телефон контакта, который уже есть в вашем кабинете. Укажите email или телефон, который не принадлежит другому адресату. |
«Call to a member function getMessage() on boolean» | Вызов осуществляется с одновременным использованием методов GET и POST. Выполняйте запрос только одним из данных методов. |
Кроме этих ошибок, могут быть и другие, указанные в описаниях конкретных методов.
Пример ответа с ошибкой:
{"error":"AK100310-02","code":"invalid_api_key"}
Коды статусов и ошибок
Коды состояния и ошибок — это число в заголовке ответа, который указывает общую классификацию ответа — например, был ли запрос успешным (200), привел ли к ошибке сервера (500), были ли проблемы с авторизацией (403), и так далее. Стандартные коды состояния обычно не требуют большого количества документации, но пользовательские коды состояния и ошибки, специфичные для API, нужны. Коды ошибок, в частности, помогают в устранении неисправных запросов.
Содержание раздела
Пример кода статуса в заголовке curl
Где перечислять HTTP-ответ и коды ошибок
Где взять коды статусов и ошибок
Как перечислить коды состояния
Коды состояния и ошибок помогают в устранении неполадок
Примеры кодов статусов и шибок
-
Context.io
-
Twitter
-
Mailchimp
-
Flickr
Практическое занятие: Коды статусов и ошибок
Пример кода статуса в заголовке curl
Коды статусов не отображаются в тебе ответа. Они содержатся в хэдере, который может быть не видим по умолчанию.
Помните, когда мы отправляли обратный вызов в разделе Создание curl запроса? Чтобы получить заголовок ответа, добавляем —include или -i к запросу curl. Если нужно, чтобы в ответе возвращался только заголовок ответа (и ничего больше), используем заглавную букву -I, например:
curl -I -X GET "https://api.openweathermap.org/data/2.5/weather?zip=95050&appid=fd4698c940c6d1da602a70ac34f0b147&units=imperial"
Заголовок ответа выглядит следующим образом:
HTTP/1.1 200 OK Server: openresty Date: Thu, 06 Dec 2018 22:58:41 GMT Content-Type: application/json; charset=utf-8 Content-Length: 446 Connection: keep-alive X-Cache-Key: /data/2.5/weather?units=imperial&zip=95050 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST
Первая строка, HTTP / 1.1 200 OK
, сообщает нам статус запроса (200
). Большинство API REST следуют стандартному протоколу для заголовков ответов. Например, 200
— это не просто произвольный код, выбранный разработчиками OpenWeatherMap API. 200
— это общепринятый код для успешного HTTP-запроса. (Если изменить метод, то получим другой код состояния.)
С помощью запроса GET довольно легко определить, успешен ли запрос, потому что получаем ожидаемый ответ. Но предположим, делаем запрос POST, PUT или DELETE, когда мы меняем данные, содержащиеся в ресурсе. Как узнать, был ли запрос успешно обработан и получен API? Коды ответа HTTP в заголовке ответа будут указывать, была ли операция успешной. Коды состояния HTTP — это просто сокращения длинных сообщений.
Коды состояния довольно тонкие, но когда разработчик работает с API, коды могут быть единственным «интерфейсом», который имеет разработчик. Если получится контролировать сообщения, которые видит разработчик, это будет большой победой юзабилити.
Слишком часто коды состояния неинформативны, плохо написаны и сообщают мало или вообще никакой полезной информации пользователю для преодоления ошибки. По большому счету, коды состояния должны помогать пользователям в восстановлении после ошибок.
Можно посмотреть список общих кодов состояния REST API здесь и общий список кодов HTTP статусов здесь. Хотя, возможно, было бы полезно включить несколько стандартных кодов состояния, нет необходимости в полном документировании всех стандартных кодов состояния, особенно если они редко запускаются в API.
Где перечислять HTTP-ответ и коды ошибок
Практичнее, если API будут иметь одну страницу с ответами и кодами ошибок ко всему API. Отдельная страница с перечнем кодов состояния (вместо добавления кода состояния в каждую конечную точку) позволяет более детально описать каждый код без переполнения других частей документации. Такой подход уменьшает избыточность и ощущение информационной перегрузки.
С другой стороны, если какие-то коды состояния и ошибок больше подходят к определенным конечным точкам, чем другие, имеет смысл вывести такие коды состояния и ошибок на страницы с описаниями конечных точек.
Такая стратегия может заключаться в том, чтобы привлечь внимание к каким-либо особенно важным кодам состояния или ошибок для конкретной конечной точки, а затем перейти к централизованной странице «Коды ответов и состояний» для получения полной информации.
Где взять коды статусов и шибок
Коды состояния и ошибок могут быть неочевидны в документации API. Вероятно, придется попросить разработчиков предоставить список всех кодов состояния и ошибок, которые уникальны для API. Иногда разработчики хардкодят коды состояния и ошибок непосредственно в программном коде, и у них нет простых способов передать полный список (что также затрудняет локализацию).
В результате может потребоваться танцы с бубнами, чтобы найти все коды. В частности, возможно придется сломать API, чтобы увидеть все возможные коды ошибок. Например, если превысить ограничение скорости для определенного запроса, API может вернуть специальную ошибку или код состояния. Такой пользовательский код обязательно нужно задокументировать. В разделе устранения неполадок в API можно специально разместить примеры получения кодов ошибок.
Как перечислить коды состояния
Коды статусов и ошибок можно привести в виде списка определений или таблицы, например так:
Status code | Значение |
---|---|
200 |
Успешный запрос и ответ |
400 |
Неверно заданные параметры или другой неверный запрос |
Коды состояния и ошибок помогают в устранении неполадок
Коды состояния и ошибок особенно полезны при устранения неполадок. Таким образом, можно рассматривать коды ошибок как дополнение к разделу по устранению неполадок.
Каждая часть документации может быть полезна в разделе, посвященном устранению неполадок. В разделе, посвященном устранению неполадок, можно описать, что происходит, когда пользователи уходят с проторенной дорожки и спотыкаются в темном лесу. Коды состояния похожи на подсказки, которые помогут пользователям вернуться на правильный путь.
В разделе по устранению неполадок можно перечислить сообщения об ошибках, связанных со следующими ситуациями:
- использование неправильных API ключей;
- использование неверных API ключей;
- параметры не соответствуют типам данных;
- API выдает исключение;
- нет данных для возврата ресурса;
- превышен предел скорости;
- параметры находятся за пределами приемлемых максимальной и минимальной границ;
- обязательный параметр отсутствует в конечной точке.
текст ошибки должен точно документироваться, чтобы она легко появлялась при поиске.
Примеры кодов статусов и ошибок
Ниже приведены несколько вариантов разделов с кодами статусов и ошибок.
Context.io
Коды статусов и ошибок Conext.io
Clearbit не только документирует стандартные коды состояния, но также описывает уникальные параметры, возвращаемые их API. Большинство разработчиков, вероятно, знакомы с кодами 200, 400 и 500, поэтому эти коды не требуют много пояснений. Но если API имеет уникальные коды, описывать их нужно адекватно и подробно.
Коды статусов и ошибок Twitter
В Twitter не только описывается код и состояние, но также предоставляется полезная информация по устранению неполадок, потенциально помогая в устранении ошибок. Например, про ошибку 500
не просто сказано, что статус относится к неработающей службе, но и есть объяснение: «Обычно это временная ошибка, например, в ситуации высокой нагрузки или если у конечной точки временно возникают проблемы. Посетите форумы разработчиков на случай, если у других возникнут аналогичные проблемы, или повторите попытку позже».
Полезные сообщения такого рода — то, к чему должны стремиться технические писатели в разделе кодов состояния (по крайней мере, делать описания тех кодов, которые указывают на проблемы)
Mailchimp
Коды статусов и ошибок Mailchimp
Mailchimp предоставляет удобочитаемые и понятные описания сообщений об ошибке. Например, в ошибке 403
вместо того, чтобы просто написать «Запрещено», Mailchimp объясняет причины, по которым можно получить ошибку запрещенного кода. У Mailchimp существует несколько типов ошибок 403. Запрос может быть запрещен из-за отключенной учетной записи пользователя или запроса, направленного не в тот центр обработки данных. В случае ошибки «WrongDataCenter» Mailchimp отмечает, что «она часто связана с неправильно настроенными библиотеками» и ссылается на дополнительную информацию о центрах обработки данных. Такой тип документации кода ошибки очень полезен для пользователей.
Flickr
Коды статусов и ошибок Flikr
В Flickr раздел «Коды ответов» встроен в описание каждой адресной темы API. Описания ошибок выглядят короткими. Хотя встраивание кодов ответов в каждую тему делает коды ошибок более заметными, в некоторых случаях такой подход менее полезен. Поскольку он встроен в каждую тему API, описания кодов ошибок должны быть краткими, иначе их содержимое будет перегружено информацией о запросе конечной точки.
Напротив, отдельная страница с перечнем кодов ошибок позволяет более подробно раскрывать каждый код, не вытесняя другую документацию. Отдельная страница также уменьшает избыточность и увеличивает объем информации.
👨💻 Практическое занятие: Коды статусов и ошибок
В своем найденном опен-сорс проекте найдем информацию о кодах статусов и ошибок. Ответим на следующие вопросы:
- присутствуют описания кодов статусов и ошибок в проекте?
- где находится информация о кодах статусов и ошибок в контексте документации? Как отдельная тема? Ниже каждой конечной точки? Где-нибудь еще?
- имеет ли API какой-либо уникальный код статусов и ошибок?
- помогают ли коды ошибок пользователям восстанавливаться после ошибок?
- сделаем запрос на одну из конечных точек, затем целенаправленно изменим параметр, чтобы сделать недействительный запрос. Какой код статуса возвращается в ответе? Этот код состояния задокументирован?
🔙
Go next ➡
Содержание:
- Общие сведения
- Формат ошибок
- Формат ответа методов API в случае ошибок
- Структура ответа
- Описание параметров
- Формат ответа методов API в случае ошибок
- Описание общих ошибок API
Общие сведения
В документе описан формат ошибок методов API, а также приведен перечень общих ошибок, которые могут возникать при обращении к методам API.
Формат ошибок
Формат ответа методов API в случае ошибок
Структура ответа
- JSON
- XML
{ "metadata":{ "status":400, "detail":"abc", "generated_at":"2015-06-18 12:37:28" }, "errors":[ { "code":281016, "title":"ошибка упрощённой отправки", "detail":"контрагент с минимальным набором данных не может быть отправителем по заказу", "link":"https://dev.dellin.ru/api/ordering/request/#error_281016", "fields":["receiver"] }, { "code":281017, "title":"Недопустимое значение параметра", "detail":"Данный параметр может содержать только значения из списка доступных значений", "link":"https://dev.dellin.ru/api/ordering/request/#error_281017", "fields":["requester"], "validValues":[1, 2, 3] }, { "code":117004, "title":"значение не найдено в справочнике", "detail":"необходимо выбрать значение из соответствующего справочника", "link":"https://dev.dellin.ru/calculation/pickup/#error_117004", "fields":["requester"], "badValues":["0xa77fcf6a449164ed490133777a68bd00"] } ] }
<response> <metadata> <status>400</status> <detail>abc</detail> <generated_at>2015-06-18 12:37:28</generated_at> </metadata> <errors> <code>281016</code> <title>ошибка упрощённой отправки</title> <detail>контрагент с минимальным набором данных не может быть отправителем по заказу</detail> <link>https://dev.dellin.ru/api/ordering/request/#error_281016</link> <fields>receiver</fields> </errors> <errors> <code>281017</code> <title>Недопустимое значение параметра</title> <detail>Данный параметр может содержать только значения из списка доступных значений</detail> <link>https://dev.dellin.ru/api/ordering/request/#error_281017</link> <fields>requester</fields> <validValues>1</validValues> <validValues>2</validValues> <validValues>3</validValues> </errors> <errors> <code>117004</code> <title>значение не найдено в справочнике</title> <detail>необходимо выбрать значение из соответствующего справочника</detail> <link>https://dev.dellin.ru/calculation/pickup/#error_117004</link> <fields>requester</fields> <badValues>0xa77fcf6a449164ed490133777a68bd00</badValues> </errors> </response>
Описание параметров
Response | ||
---|---|---|
Параметр | Тип | Описание |
metadata | object | Информация об оформленной заявке |
metadata.status | integer |
Эмуляция http-кода состояния |
metadata.detail | string | Текстовое описание ответа сервера |
metadata.generated_at | string | Дата и время генерации ответа сервера |
errors | array of Response.Errors | Перечень ошибок |
Response.Errors | ||
---|---|---|
Параметр | Тип | Описание |
code | integer | Номер ошибки |
title | string |
Краткое описание ошибки |
detail | string | Детальное описание ошибки |
link | string | Ссылка на документацию |
fields | array of string | Список параметров в запросе к методу, вызвавших ошибку |
validValues | array of string | Список доступных значений параметра |
badValues | array of string | Список ошибочных значений, переданных в параметре |
Описание общих ошибок API
Номер ошибки | http-код | Краткое описание ошибки | Детальное описание ошибки |
---|---|---|---|
100001 |
415 | Некорректный content-type | Допустимые значения content-type: application/json (стандарт RFC4627) и text/xml (стандарт RFC3023) |
100002 |
404 | Метод не найден | Проверьте правильность адреса метода |
100003 |
410 | Метод отключен | Запрошенный метод более не доступен |
100004 |
403 | Отсутствует доступ к методу | Доступ к методу предоставляется по требованию. Для получения доступа обратитесь к персональному менеджеру или в техническую поддержку |
100005 |
429 | Количество запросов к превышено | Превышена допустимая частота запросов. Для увеличения лимита обратитесь к персональному менеджеру или в техническую поддержку |
100006 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
101001 |
401 | Требуется авторизация | Не передан API-ключ |
101002 |
401 | Требуется авторизация | Передан недействительный API-ключ |
101003 |
401 | Требуется авторизация | Требуется передать параметр sessionID |
101004 |
401 | Требуется авторизация | Время жизни сессии истекло |
101005 |
401 | Требуется авторизация | Сессия не найдена или создана с другим API-ключом |
101006 |
401 | Требуется авторизация | Неверный логин или пароль |
101007 |
401 | Требуется авторизация | API-ключ заблокирован. Обратитесь в техническую поддержку |
101008 |
401 | Ошибка парсинга | Запрос не соответствует формату json |
101009 |
401 | Ошибка парсинга | Запрос не соответствует формату xml |
110001 |
400 | Неверный формат параметра | Значение, переданное в параметре, не соответствует требуемому формату |
110002 |
400 | Ошибка типизации | Значение, переданное в параметре, имеет некорректный тип |
110003 |
400 | Отсутствует обязательный параметр | Отсутствует обязательный параметр |
110004 |
400 | Не передан ни один из обязательных параметров | В запросе должен присутствовать хотя бы один параметр из совокупности, однако не указано ни одного |
110005 |
400 | Допустима передача только одного из параметров | Указаны взаимоисключающие параметры, только один из которых может присутствовать в запросе |
110006 |
400 | Превышено ограничение на длину списка | Количество элементов в списке превышает максимально допустимое |
110007 |
400 | Объект не существует | Не найден объект с указанным ID. Проверьте правильность переданного значения |
110008 |
400 | Недопустимый набор параметров | Указанные параметры не должны участвовать в запросе |
120001 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomers не отвечает) |
120002 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomers вернул неизвестную ошибку) |
120101 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomersV2 не отвечает) |
120102 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис calculateCustomersV2 вернул неизвестную ошибку) |
120201 |
400 | Ошибка в параметрах запроса |
Переданы неправильные параметры в запрос (Причина: Переданы некорректные данные в getOrdersTracker) |
120301 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки (Причина: Сервис getPaymentsByOrders не отвечает) |
121001 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
121002 |
500 | Внутренняя ошибка сервера |
Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
130001 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
130002 |
400 | Ошибка выполнения запроса | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130003 |
400 | Указан некорректный документ, удостоверяющий личность | Проверьте правильность переданных значений |
130004 |
400 | Не передан ни один из обязательных параметров |
В запросе должен присутствовать хотя бы один параметр из совокупности, однако не указано ни одного |
130005 |
400 | Отсутствует обязательный параметр | Отсутствует обязательный параметр |
130006 |
400 | Значение превышает допустимое | Габариты превышают допустимые размеры |
130007 |
400 | Неверный формат параметра | Значение, переданное в параметре, не соответствует требуемому формату |
130008 |
400 |
Недопустимое значение параметра | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130009 |
400 | Превышено ограничение на длину значения | Превышена максимально допустимая длина значения поля |
130010 |
400 | Отсутствует согласие с тарифами и правилами перевозки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130014 |
400 | Ошибка наложенного платежа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130015 |
400 | Ошибка оформления услуги | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130017 |
400 | Невозможно оформить заявку на указанное время | — |
130021 |
400 | Услуга недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130022 |
400 | Указан некорректный адрес | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
130023 |
400 | Выбран недопустимый терминал | Выбран недопустимый терминал |
130024 |
400 | Превышено ограничение на длину списка | Превышено максимальное количество контрагентов в адресной книге (10000). Необходимо удалить часть записей или обратиться в службу поддержки |
150001 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
150002 |
500 | Внутренняя ошибка сервера | Попробуйте вызвать метод через некоторое время. При повторении ошибки обратитесь в службу поддержки |
180001 |
400 | Указан некорректный документ, удостоверяющий личность | Проверьте правильность переданных значений |
180002 |
400 | Указан некорректный адрес | Указан некорректный адрес |
180003 |
400 | Выбран недопустимый терминал | Выбранный терминал не может принять груз с указанными ВГХ |
180004 |
400 | Услуга недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180005 |
400 | Значение превышает допустимое | Весогабаритные характеристики груза превышают допустимые для приёма на терминалах города |
180006 |
400 | Ошибка в параметрах запроса | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180007 |
400 | Недопустимое значение параметра | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180008 |
400 | Ошибка упрощенной отправки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180009 |
400 | Ошибка оформления услуги Доставка в день заказа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180010 |
400 | Ошибка оформления услуги Доставка в точное время | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180011 |
400 | Указан некорректный период работы | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180012 |
400 | Выбранная дата недоступна | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180013 |
400 | Ошибка параметров оплаты | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180014 |
400 | Ошибка наложенного платежа | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180015 |
400 | Ошибка оформления услуги | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180016 |
400 | Ошибка при сохранении заявки | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
180017 |
400 | Невозможно оформить заявку на указанное время | Детальное описание ошибки содержит уточняющую информацию. Текст варьируется в зависимости от проверяемого параметра |
Может быть много способов обработки ответов API, поступающих от серверов в Android, но используете ли вы хороший способ обработки? В этой статье мы увидим, как ответы API обрабатываются с помощью Kotlin Sealed Class при использовании библиотеки Retrofit для вызовов API. Этот подход с закрытым классом идеально подходит в этом случае: если пользователь ожидает данные в пользовательском интерфейсе, нам нужно отправить ошибки в наш пользовательский интерфейс, чтобы уведомить пользователя и убедиться, что пользователь не просто увидит пустой экран. или столкнуться с неожиданным пользовательским интерфейсом.
Сделать запечатанный класс
Просто создайте универсальный запечатанный класс с именем Resource в отдельном файле в вашем пакете данных или пакете utils/others. Мы назвали его Resource и сделали его универсальным, потому что мы будем использовать этот класс для переноса наших различных типов ответов API. По сути, нам нужно обновить наш пользовательский интерфейс для этих трех событий, т. е. Success, Error, Loading. Итак, мы создали их как дочерние классы ресурсов для представления различных состояний пользовательского интерфейса. Теперь, в случае успеха, мы получим данные, поэтому обернем их Resource. Успех и в случае ошибки мы завернем сообщение об ошибке в Resource. Ошибка, и в случае загрузки мы просто вернем объект Resource.Loading (вы также можете изменить его, чтобы обернуть данные загрузки в соответствии с вашими потребностями).
Kotlin
sealed
class
Resource<T>(
val data: T? =
null
,
val message: String? =
null
) {
class
Success<T>(data: T) : Resource<T>(data = data)
class
Error<T>(errorMessage: String) : Resource<T>(message = errorMessage)
class
Loading<T> : Resource<T>()
}
Давайте также взглянем на наш интерфейс ApiService (см. чуть ниже), здесь в каждой приостановленной функции вызова API мы оборачиваем наш ответ на вызов API классом Retrofit Response, потому что он предоставляет нам дополнительную информацию о вызовах API. Например: будут ли вызовы успешными или нет, код ошибки и т. д. Мы будем вызывать эти приостановленные функции из наших репозиториев… мы увидим это через какое-то время. Не запутайтесь в нашем пользовательском классе Resource и классе Response Retrofit. Класс Resource просто представляет различные состояния пользовательского интерфейса, в то время как класс Response Retrofit дает нам дополнительную информацию о вызовах API.
Kotlin
interface
ExampleApiService {
@GET
(
"example/popular_articles"
)
suspend fun fetchPopularArticles(): Response<PopularArticlesResponse>
@GET
(
"example/new_articles"
)
suspend fun fetchNewArticles(): Response<NewArticlesResponse>
}
Теперь давайте перейдем к основной части обработки успеха/ошибки API.
У вас должны быть разные репозитории в вашем проекте в соответствии с вашими потребностями, и все ваши вызовы API должны происходить через эти репозитории, нам нужно выполнять обработку ошибок каждого вызова API. Итак, нам нужно обернуть каждый вызов API внутри блока try-catch. Но подождите… писать блок try-catch для каждого вызова API — плохая идея. Итак, давайте создадим BaseRepository и напишем там общую функцию приостановки с именем safeApiCall (любое имя, которое вам нравится), которая будет отвечать за обработку ответов API на каждый вызов API.
Kotlin
abstract
class
BaseRepo() {
suspend fun <T> safeApiCall(apiToBeCalled: suspend () -> Response<T>): Resource<T> {
return
withContext(Dispatchers.IO) {
try
{
val response: Response<T> = apiToBeCalled()
if
(response.isSuccessful) {
Resource.Success(data = response.body()!!)
}
else
{
val errorResponse: ExampleErrorResponse? = convertErrorBody(response.errorBody())
Resource.Error(errorMessage = errorResponse?.failureMessage ?:
"Something went wrong"
)
}
}
catch
(e: HttpException) {
Resource.Error(errorMessage = e.message ?:
"Something went wrong"
)
}
catch
(e: IOException) {
Resource.Error(
"Please check your network connection"
)
}
catch
(e: Exception) {
Resource.Error(errorMessage =
"Something went wrong"
)
}
}
}
private
fun convertErrorBody(errorBody: ResponseBody?): ExampleErrorResponse? {
return
try
{
errorBody?.source()?.let {
val moshiAdapter = Moshi.Builder().build().adapter(ExampleErrorResponse::
class
.java)
moshiAdapter.fromJson(it)
}
}
catch
(exception: Exception) {
null
}
}
}
safeApiCall имеет приостановленную лямбда-функцию с именем «api», которая возвращает данные, завернутые в Resource, чтобы safeApiCall мог получать каждую нашу функцию вызова API. Поскольку safeApiCall является функцией приостановки, мы используем область сопрограммы withContext с Dispatchers.IO, таким образом, все сетевые вызовы будут выполняться в фоновом потоке ввода/вывода. Всякий раз, когда сетевой вызов терпит неудачу, он выдает исключение, поэтому нам нужно вызвать нашу лямбда-функцию «apiToBeCalled» внутри блока try-catch, и здесь, если вызов API будет успешным, мы завернем наши данные об успешном ответе в Resource. Объект Success, и мы вернем его через safeApiCall, и если вызов API завершится неудачно, мы перехватим это конкретное исключение в одном из следующих блоков catch:
- Когда что-то пойдет не так на сервере при обработке HTTP-запроса, он выдаст исключение HTTP.
- Когда у пользователя не будет действительного подключения к Интернету/Wi-Fi, он выдаст исключение ввода-вывода Java.
- Если произойдет какое-либо другое исключение, оно просто появится в общем блоке исключений (поместите этот блок последним). Вы также можете добавить другой блок перехвата исключений в соответствии с вашим конкретным случаем перед последним общим блоком перехвата исключений.
Теперь в блоках catch мы можем просто отправлять собственные сообщения об ошибках или сообщения об ошибках, полученные из исключений в объекте Resource.Error.
Примечание. В случае, если сетевой вызов не завершается ошибкой и вместо этого не выдает исключение, если он отправляет свой собственный ответ json об ошибке (иногда это происходит в случае сбоя аутентификации / истечения срока действия токена / недействительного ключа API / и т. д.), тогда мы выиграли ’ не получите исключение, вместо этого класс Response Retrofit поможет нам определить, возвращая true/false в response.isSuccessful. В приведенном выше коде мы также обрабатываем этот внутренний блок try, если он возвращает false, то внутри другого случая мы будем анализировать собственный пользовательский ответ json об ошибке API (см. Пример ниже) в созданный нами класс pojo, т.е. ExampleErrorResponse. Для разбора этого json-ответа на наш pojo ExampleErrorResponse мы используем библиотеку Moshi.в нашей функции convertErrorBody, которая вернет объект ExampleErrorResponse, а затем мы сможем получить собственный ответ API об ошибке.
{ “status”: ”UnSuccessful”, “failure_message” : ”Invalide api key or api key expires” }
Итак, в конечном итоге наша функция safeApiCall вернет наш ответ, завернутый в класс Resource. это может быть либо успех, либо ошибка. Если вы поняли до этого, то привет вам. Вы выиграли большую часть битвы.
Теперь давайте изменим способ вызова Apis в ваших текущих репозиториях.
Мы расширим все наши репозитории из BaseRepo, чтобы мы могли получить доступ к функции safeApiCall. И внутри функций нашего репозитория, таких как getPopularArticles, мы передадим нашу функцию ApiService (например: apiService.fetchPopularArticles()) в качестве лямбда-аргумента в safeApiCall, а затем safeApiCall просто обработает свою часть успеха/ошибки, после чего в конечном итоге вернет Resource это дойдет до пользовательского интерфейса через ViewModel.
Примечание. Помните, что когда мы будем вызывать функцию getPopularArticles в модели представления (см. пример ниже), то сначала мы разместим объект Resource.Loading в изменяемых оперативных данных, чтобы наш фрагмент/активность мог наблюдать за этим состоянием загрузки и мог обновить пользовательский интерфейс, чтобы показать загрузку. состояние, а затем, когда мы подготовим наш ответ API, он будет снова опубликован в изменяемых оперативных данных, чтобы пользовательский интерфейс мог снова получить этот ответ (успех/ошибка) и может отображать данные в пользовательском интерфейсе или обновлять себя соответственно.
Kotlin
class
ExampleRepo(
private
val apiService: ExampleApiService) : BaseRepo() {
suspend fun getPopularArticles() : Resource<PopularArticlesResponse> {
return
safeApiCall { apiService.fetchPopularArticles() }
}
suspend fun getPublishedArticles() : Resource<NewlyPublishedArticlesResponse> = safeApiCall { apiService.fetchPublishedArticles() }
}
ViewModel
Kotlin
class
ExampleViewModel (
private
val exampleRepo: ExampleRepo) {
private
val _popularArticles = MutableLiveData<Resource<PopularArticlesResponse>>()
val popularArticles: LiveData<Resource<PopularArticlesResponse>> = _popularArticles
init {
getPopularArticles()
}
private
fun getPopularArticles() = viewModelScope.launch {
_popularArticles.postValue(Resource.Loading())
_popularArticles.postValue(exampleRepo.getPopularArticles())
}
}
Вот и все, теперь мы можем легко наблюдать все состояния, т.е. Успех, Ошибка, Загрузка в нашем пользовательском интерфейсе. Это было об обработке ошибок API или об обработке ответов API в лучшем виде.
замечания
Каждый поток будет иметь свой последний код ошибки. Windows API установит последний код ошибки в вызывающем потоке.
Вы всегда должны вызывать GetLastError
сразу после проверки возвращаемого значения функции API Windows.
Большинство функций Windows API устанавливают последний код ошибки, когда они терпят неудачу. Некоторые также установят последний код ошибки, когда они преуспеют. Существует ряд функций, которые не устанавливают последний код ошибки. Всегда обращайтесь к документации по функциям Windows API.
Невозможно использовать FORMAT_MESSAGE_FROM_SYSTEM
без FORMAT_MESSAGE_IGNORE_INSERTS
при использовании функции FormatMessage
для получения описания кода ошибки.
Вступление
API Windows предоставляется с помощью C-вызываемого интерфейса. Успех или сбой вызова API сообщаются строго через возвращаемые значения. Исключения не являются частью документированного контракта (хотя некоторые реализации API могут вызывать исключения SEH , например, при передаче аргумента lpCommandLine только для чтения в CreateProcess ).
Сообщение об ошибке грубо относится к одной из четырех категорий:
- Только возвращаемое значение
- Возвращаемое значение с дополнительной информацией о сбое
- Возвращаемое значение с дополнительной информацией об отказе и успехе
- Возвращаемое значение
HRESULT
Документация для каждого вызова API явно вызывается, как сообщаются ошибки. Всегда обращайтесь к документации.
Ошибка, сообщенная только возвратным значением
Некоторые вызовы API возвращают единый флаг отказа / успеха без какой-либо дополнительной информации (например, GetObject ):
if ( GetObjectW( obj, 0, NULL ) == 0 ) {
// Failure: no additional information available.
}
Сообщается об ошибке с сообщением об ошибке
В дополнение к возвращаемому значению отказа / успеха некоторые вызовы API также устанавливают последнюю ошибку при сбое (например, CreateWindow ). Документация обычно содержит следующую стандартную формулировку для этого случая:
Если функция завершается успешно, возвращаемое значение <значение успеха API> .
Если функция не работает, возвращаемое значение <значение ошибки API> . Чтобы получить расширенную информацию об ошибке, вызовите GetLastError .
if ( CreateWindowW( ... ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: must not call GetLastError.
}
Очень важно, что вы вызываете GetLastError()
НЕМЕДЛЕННО. Последний код ошибки может быть перезаписан любой другой функцией, поэтому, если есть дополнительная функция вызова между неудавшейся функцией и вызовом GetLastError()
, возврат из GetLastError()
больше не будет надежным. Будьте особенно осторожны при работе с конструкторами C ++.
Как только вы получите код ошибки, вам нужно будет его интерпретировать. Вы можете получить полный список кодов ошибок в MSDN на странице Системные коды ошибок (Windows) . Кроме того, вы можете посмотреть в своих файлах заголовков системы; файл со всеми константами кода ошибки — winerror.h
. (Если у вас есть официальный SDK от Microsoft для Windows 8 или новее, это находится в shared
папке с папкой include.)
Заметки о вызове GetLastError()
на других языках программирования
.net (C #, VB и т. д.)
С .net вы не должны P / Invoke в GetLastError()
напрямую. Это связано с тем, что среда выполнения .net сделает другие вызовы Windows API одним и тем же потоком за вашей спиной. Например, сборщик мусора может вызвать VirtualFree()
если он найдет достаточно памяти, которую он больше не использует, и это может произойти между вашим назначенным вызовом функции и вашим вызовом GetLastError()
.
Вместо этого .net предоставляет Marshal.GetLastWin32Error()
, которая будет извлекать последнюю ошибку из последнего вызова P / Invoke, который вы сами сделали. Используйте это вместо прямого вызова GetLastError()
.
(.net, похоже, не мешает вам импортировать GetLastError()
любом случае, я не уверен, почему.)
Идти
Различные средства, предоставляемые Go для вызова DLL-функций (которые находятся как в syscall
пакета, syscall
и в пакете golang.org/x/sys/windows
), возвращают три значения: r1
, r2
и err
. r2
никогда не используется; вы можете использовать пустой идентификатор. r1
— возвращаемое значение функции. err
является результатом вызова GetLastError()
но преобразуется в тип, реализующий error
, поэтому вы можете передать его вызывающим функциям для обработки.
Поскольку Go не знает, когда вызывать GetLastError()
а когда нет, он всегда будет возвращать ошибку nil
. Поэтому типичная идиома обработки ошибок Go
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
// handle err
}
// use r1
не будет работать. Вместо этого вы должны проверить r1
точно так же, как и на C, и использовать только err
если это указывает, что функция возвратила ошибку:
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
// handle err
}
// use r1
Сообщается об ошибке с дополнительной информацией о сбоях и успехах
Некоторые вызовы API могут преуспеть или сбой более чем одним способом. API обычно возвращают дополнительную информацию как для успешных вызовов, так и для ошибок (например, CreateMutex ).
if ( CreateMutexW( NULL, TRUE, L"Global\MyNamedMutex" ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: Determine which mutex was returned.
if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
// Existing mutex object returned.
} else {
// Newly created mutex object returned.
}
}
Ошибка, сообщенная как значение HRESULT
HRESULT s — числовые 32-битные значения, где биты или диапазоны бит кодируют четко определенную информацию. MSB — это флаг отказа / успеха, а остальные бит хранят дополнительную информацию. Отказ или успех можно определить с помощью макросов FAILED или SUCCEEDED . HRESULT
s обычно используются совместно с COM, но также отображаются в реализациях, отличных от COM (например, StringCchPrintf ).
const size_t cchBuf = 5;
wchar_t buffer[cchBuf] = { 0 };
HRESULT hr = StringCchPrintfW( buffer, cchBuf, L"%s", L"Hello, world!" );
if ( FAILED( hr ) ) {
// Failure: Determine specific reason.
switch ( hr ) {
case STRSAFE_E_INSUFFICIENT_BUFFER:
// Buffer too small; increase buffer and retry.
...
case STRSAFE_E_INVALID_PARAMETER:
// Invalid parameter; implement custom error handling (e.g. logging).
...
default:
// Some other error code; implement custom error handling (e.g. logging).
...
}
}
Преобразование кода ошибки в строку сообщения
GetLastError
возвращает числовой код ошибки. Чтобы получить описательное сообщение об ошибке ( например , для отображения пользователю), вы можете вызвать FormatMessage
:
// This functions fills a caller-defined character buffer (pBuffer)
// of max length (cchBufferLength) with the human-readable error message
// for a Win32 error code (dwErrorCode).
//
// Returns TRUE if successful, or FALSE otherwise.
// If successful, pBuffer is guaranteed to be NUL-terminated.
// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength)
{
if (cchBufferLength == 0)
{
return FALSE;
}
DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, /* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
pBuffer,
cchBufferLength,
NULL);
return (cchMsg > 0);
}
В C ++ вы можете значительно упростить интерфейс, используя класс std::string
:
#include <Windows.h>
#include <exception>
#include <stdexcept>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;
String GetErrorMessage(DWORD dwErrorCode)
{
LPTSTR psz = NULL;
const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPTSTR>(&psz),
0,
NULL);
if (cchMsg > 0)
{
// Assign buffer to smart pointer with custom deleter so that memory gets released
// in case String's c'tor throws an exception.
auto deleter = [](void* p) { ::HeapFree(::GetProcessHeap(), 0, p); };
std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
return String(ptrBuffer.get(), cchMsg);
}
else
{
throw std::runtime_error("Failed to retrieve error message string.");
}
}
ПРИМЕЧАНИЕ. Эти функции также работают для значений HRESULT
. Просто измените первый параметр из DWORD dwErrorCode
на HRESULT hResult
. Остальная часть кода может оставаться неизменной.