Выполнение POST-запросов и загрузки файлов с помощью URLSession

За прошедшие годы встроенный в Foundation URLSession API превратился в универсальный и очень мощный сетевой инструмент, настолько, что часто сторонние библиотеки теперь больше не требуются для выполнения стандартных сетевых вызовов HTTP простым и понятным способом.

Хотя многие из удобных API-интерфейсов, с которыми поставляется URLSession, ориентированы на GET-запросы, используемые для получения данных, в этой статье давайте рассмотрим, как именно можно использовать и другие методы HTTP, в частности, как различные типы POST-запросов могут выполняться без любых внешних зависимостей.

Данные и задачи загрузки

Возможно, самый простой способ использовать URLSession для выполнения POST- запроса — это использовать основанные на URLRequest перегрузке различных API-интерфейсов dataTask (которые поддерживают делегаты и колбеки, работающие на клоужерах, а также Combine). Помимо прочего, URLRequest позволяет нам настроить, какой httpMethod должен использовать данный сетевой вызов, а также другие полезные параметры, например, какие данные httpBody отправлять и какой cachePolicy использовать. Например:

struct Networking {
    var urlSession = URLSession.shared

    func sendPostRequest(
        to url: URL,
        body: Data,
        then handler: @escaping (Result<Data, Error>) -> Void
    ) {
        // To ensure that our request is always sent, we tell
        // the system to ignore all local cache data:
        var request = URLRequest(
            url: url,
            cachePolicy: .reloadIgnoringLocalCacheData
        )
        
        request.httpMethod = "POST"
request.httpBody = body

        let task = urlSession.dataTask(
            with: request,
            completionHandler: { data, response, error in
                // Validate response and call handler
                ...
            }
        )

        task.resume()
    }
}

В зависимости от сервера, на который отправляется наш POST-запрос, мы также можем при желании дополнительно настроить наш экземпляр URLRequest, предоставив ему, например, заголовок Content-Type.

В качестве альтернативы мы могли бы вместо этого использовать API uploadTask для создания собственной задачи запроса, позволяющей нам загружать данные, пока приложение находится в фоновом режиме, и обеспечивающей встроенную поддержку для прикрепления массива данных непосредственно к самой задаче:

struct Networking {
    var urlSession = URLSession.shared

    func sendPostRequest(
        to url: URL,
        body: Data,
        then handler: @escaping (Result<Data, Error>) -> Void
    ) {
        var request = URLRequest(
            url: url,
            cachePolicy: .reloadIgnoringLocalCacheData
        )
        
        request.httpMethod = "POST"

        let task = urlSession.uploadTask(
    with: request,
    from: body,
    completionHandler: { data, response, error in
        // Validate response and call handler
        ...
    }
)

        task.
resume() } }
 Наблюдение за обновлениями прогресса

Хотя любой из двух вышеперечисленных подходов будет отлично работать при отправке меньших объемов данных как часть POST-запроса, иногда нам может потребоваться загрузить файл, который может быть довольно большим (даже что-то простое, например изображение, может весить несколько мегабайт). При этом мы, скорее всего захотим чтобы пользователь мог отслеживать обновление прогресса в реальном времени, иначе пользовательский интерфейс нашего приложения может работать медленно или даже совсем не отвечать на запрос.

К сожалению, ни один из API-интерфейсов URLSession, работающих на замыканиях или Combine, не предлагает прямой поддержки для наблюдения за текущим прогрессом запроса, но, к счастью, это то, что мы можем довольно легко реализовать, используя старый добрый шаблон делегирования.

Чтобы наглядно это продемонстрировать, давайте создадим класс FileUploader (он должен быть подклассом NSObject Objective-C).

Затем мы будем использовать собственный экземпляр URLSession, а не shared, и это позволит нам стать делегатом этой сессии. Затем мы определим API, который позволит нам загрузить файл с заданного локального URL-адреса, и далее мы позволим вызовам этого API быть переданными в виде двух клоужеров — один для обработки прогресса, другой — стандартный завершающий обработчик. Наконец, мы сохраним все обработчики прогресса загрузки в словаре на основе идентификатора загрузки, чтобы позже мы могли вызывать эти клоужеры в нашей реализации протокола делегата:

class FileUploader: NSObject {
    // We'll define a few type aliases to make our code easier to read:
    typealias Percentage = Double
    typealias ProgressHandler = (Percentage) -> Void
    typealias CompletionHandler = (Result<Void, Error>) -> Void

    // Creating our custom URLSession instance. We'll do it lazily
    // to enable 'self' to be passed as the session's delegate:
    private lazy var urlSession = URLSession(
        configuration: .
default, delegate: self, delegateQueue: .main ) private var progressHandlersByTaskID = [Int : ProgressHandler]() func uploadFile( at fileURL: URL, to targetURL: URL, progressHandler: @escaping ProgressHandler, completionHandler: @escaping CompletionHandler ) { var request = URLRequest( url: targetURL, cachePolicy: .reloadIgnoringLocalCacheData ) request.httpMethod = "POST" let task = urlSession.uploadTask( with: request, fromFile: fileURL, completionHandler: { data, response, error in // Validate response and call handler ... } ) progressHandlersByTaskID[task.taskIdentifier] = progressHandler task.resume() } }

Затем давайте реализуем протокол URLSessionTaskDelegate, который представляет собой специализированную версию базового протокола URLSessionDelegate, который добавляет несколько дополнительных методов, позволяющих нам отслеживать события, связанные с конкретной задачей. В этом случае мы хотим получать уведомления только о том, когда был обновлен ход выполнения данной задачи URLSessionTask, и это можно сделать, реализовав следующий метод:

extension FileUploader: URLSessionTaskDelegate {
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didSendBodyData bytesSent: Int64,
        totalBytesSent: Int64,
        totalBytesExpectedToSend: Int64
    ) {
        let progress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
        let handler = progressHandlersByTaskID[task.taskIdentifier]
        handler?(progress)
    }
}

С учетом вышеизложенного теперь мы сможем использовать процентные значения, передаваемые в каждый клоужер progressHandler для управления любым компонентом пользовательского интерфейса, который мы хотим использовать для визуализации хода загрузки, например, ProgressView, UIProgressView или NSProgressIndicator.

Показатель загрузки процесса во времени

Наконец, давайте также посмотрим, как можно преобразовать указанный выше FileUploader для использования Combine вместо нескольких замыканий. В конце концов, дизайн Combine, ориентированный на «значения с течением времени», идеально подходит для моделирования обновлений прогресса, поскольку мы хотим отправить какие-то процентные значения с течением времени, а затем закончить событие завершением, что и является тем, что делает publisher Combine.

Хотя мы могли бы реализовать эту функцию с помощью кастомного publisher, давайте в данном случае воспользуемся CurrentValueSubject, который предоставляет встроенный способ отправки значений, которые затем кэшируются и отправляются каждому новому подписчику. Таким образом, мы можем связать каждую задачу загрузки с заданным субъектом (точно так же, как мы ранее хранили каждый клоужер progressHandler), а затем вернуть этот субъект в качестве publisher с помощью API eraseToAnyPublisher — например:

class FileUploader: NSObject {
    typealias Percentage = Double
    typealias Publisher = AnyPublisher<Percentage, Error>
    
    private typealias Subject = CurrentValueSubject<Percentage, Error>

    private lazy var urlSession = URLSession(
        configuration: .
default, delegate: self, delegateQueue: .main ) private var subjectsByTaskID = [Int : Subject]() func uploadFile(at fileURL: URL, to targetURL: URL) -> Publisher { var request = URLRequest( url: targetURL, cachePolicy: .reloadIgnoringLocalCacheData ) request.httpMethod = "POST" let subject = Subject(0) var removeSubject: (() -> Void)? let task = urlSession.uploadTask( with: request, fromFile: fileURL, completionHandler: { data, response, error in // Validate response and send completion ... subject.send(completion: .finished) removeSubject?() } ) subjectsByTaskID[task.taskIdentifier] = subject removeSubject = { [weak self] in self?.
subjectsByTaskID.removeValue(forKey: task.taskIdentifier) } task.resume() return subject.eraseToAnyPublisher() } }

Теперь все, что осталось — это обновить нашу реализацию URLSessionTaskDelegate для отправки каждого значения прогресса субъекту, связанному с рассматриваемой задачей, вместо вызова замыкания:

extension FileUploader: URLSessionTaskDelegate {
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didSendBodyData bytesSent: Int64,
        totalBytesSent: Int64,
        totalBytesExpectedToSend: Int64
    ) {
        let progress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
        let subject = subjectsByTaskID[task.taskIdentifier]
        subject?.send(progress)
    }
}

Теперь мы можем легко выполнять как более простые POST-запросы, так и загрузку файлов с отображением прогресса загрузки, используя либо Combine, либо API на основе замыканий. Классяче!

Оригинал статьи

Как сделать POST-запрос с помощью cURL на Ubuntu / Debian

Содержание

  1. Что такое cURL?
  2. Установка cURL на Ubuntu / Debian
  3. Как сделать POST-запрос с помощью cURL на Ubuntu / Debian
  4. Заключение

Здравствуйте, друзья. Команда cURL — это полезная команда особенно для сетевых подключений в терминале. В этом посте вы узнаете, как сделать POST-запрос с помощью cURL. Таким образом, вы сможете быстро выполнить несколько тестов по отправке данных.

Что такое cURL?

Согласно сайту проекта

CURL — ЭТО ИНСТРУМЕНТ КОМАНДНОЙ СТРОКИ И БИБЛИОТЕКА ДЛЯ ПЕРЕДАЧИ ДАННЫХ С ПОМОЩЬЮ URL (АДРЕСОВ).

Также это инструмент не имеющий графического интерфейса, но простой в использовании. Кроме того, он имеет открытый исходный код, что позволяет легко проводить аудит ПО.

cURL доступен практически в любом дистрибутиве Linux, а в некоторых из них даже установлен по умолчанию. Поэтому получить его не составит труда.

Данный инструмент используется в командных строках или скриптах для передачи данных. Его применение простирается от компьютеров, до автомобилей, телевизоров и других устройств, которым он необходим.

И так поехали.

Установка cURL на Ubuntu / Debian

Как я уже говорил, cURL доступен практически для любой системы и лучше всего его устанавливать из официальных репозиториев. Итак, чтобы установить его, откройте терминал и выполните следующие команды.

sudo apt update

sudo apt upgrade

Это так быстро и просто.

Теперь проверьте установленную версию.

curl --version

Образец ответа терминала.

Выполнение POST-запроса с помощью cURL на Ubuntu / Debian

Теперь мы можем продолжить.

Как сделать POST-запрос с помощью cURL на Ubuntu / Debian

Идея заключается в отправке данных с помощью HTTP POST. Это очень удобно для скриптов и конфигураций. В целом синтаксис выглядит следующим образом.

curl -X POST [options] [URL]

Не обязательно, но обычно POST-запрос выполняется при использовании HTML-форм.

Если вы хотите отправить некоторые данные подобным образом, вы также можете использовать параметр -d.

curl -d "name=Angelo&website=unixcop" -X POST https://setiwik.ru/

В этом случае информация отправляется на определенный адрес. Для этого запрашивается Имя и Веб-сайт. Как видите, таким образом, включаются некоторые данные. Для этого обратите внимание, что вам нужен точный адрес получателя данных.

Существует опция -H, с помощью которой вы можете указать формат, в котором будут отправлены данные. Например, если вы хотите отправить информацию в HTML-форме с форматом application/x-www-form-urlencoded, то нужно указать его.

curl -X POST https://setiwik.ru/form -H "Content-Type: application/x-www-form-urlencoded" -d "name=Angelo1&[email protected]"

Таким образом, cURL уже знает в каком формате передавать данные. Другой пример с этим же параметром — в формате JSON.

curl -X POST https://setiwik.ru/json -H 'Content-Type: application/json' -d '{"name": "angelo", "password": "21125"}'.

Повторяю, вы должны знать в каком формате сайт будет обрабатывать запрос, прежде чем определять команду curl.

Еще один полезный пример — обработка XML-файлов.

curl -X POST https://setiwik.ru/echo/post/xml -H "Content-Type: application/xml" -d "<Data><Id>1</Id><Username>setiwik</Username></Data>"

Или отправить файл. Для этого случая curl по умолчанию включает отправку формата в зависимости от файла, но вы должны указать полный путь к файлу. Вы также можете указать конкретный тип с помощью опции -H.

curl -d @[путь] https://setiwik.ru/server

Как видите, процесс прост.

Заключение

В этом посте вы узнали как использовать cURL для отправки данных с помощью POST. Надеюсь вам понравилось и вы сможете применить это в своих проектах.

rest — Ошибка при выполнении почтового запроса весной mvc

Задавать вопрос

спросил

Изменено 7 лет, 4 месяца назад

Просмотрено 1к раз

При выполнении следующего почтового запроса в почтальоне:

 http://localhost:8080/FinalSmsApi/rest/requestSms/hello
 

с параметрами имя пользователя, пароль и телефон. Я получаю следующую ошибку:

 HTTP-статус 415: сервер отклонил этот запрос, поскольку объект запроса находится в формате, не поддерживаемом запрошенным ресурсом для запрошенного метода.
 

Это контроллер:

 @RestController
открытый класс MainController1 {
    @RequestMapping (значение = "/ привет", метод = POST, потребляет = "приложение/json")
    public void Register(@RequestParam(value = "username") Строковое имя пользователя,
                         @RequestParam(value = "password") Строковый пароль,
                         @RequestParam(value = "phone") String phone) {...}
}
 

Использование версии Spring 4.

  • пружина
  • остальные
  • пружина-MVC

Состояние HTTP 415: сервер отклонил этот запрос…

Это означает, что ваша конечная точка не может обработать переданное тело запроса . Эта ошибка имеет две основные причины: либо вы , а не указали тип тела вашего запроса, либо вы передали неверные данные.

Путем добавления Content-Type в заголовки вашего запроса, эта проблема будет решена:

 Content-Type: application/json
 

Кроме того, вы не фиксируете тело запроса в методе public void Register(..) . Если вы планируете пойти по этому пути, лучше отказаться от атрибута Consumers и передать все параметры с Query Parameters , как вы это сделали.

Другой подход заключается в определении класса ресурсов, например:

 public class User {
    частное строковое имя пользователя;
    закрытый строковый пароль;
    личный телефон String;
    // геттеры и сеттеры
}
 

Затем измените свой контроллер для захвата тела запроса, например:

 @RequestMapping (значение = "/ привет", метод = POST, потребляет = "приложение/json")
public void Register(@RequestBody User user) {...}
 

И, наконец, отправьте представление JSON вместе с вашим запросом:

 curl -XPOST -H'Content-Type: application/json' --data '{"username": "", "password": "" , "телефон": ""}' http://localhost:8080/hello
 
7

Зарегистрируйтесь или войдите в систему

Зарегистрируйтесь с помощью Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя электронную почту и пароль

Опубликовать как гость

Электронная почта

Обязательно, но не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания и подтверждаете, что прочитали и поняли нашу политику конфиденциальности и кодекс поведения.

Отправка запроса POST в REST API | Junos OS

Используйте запрос HTTP POST для отправки одного или нескольких запросов RPC к REST API. Вы можете использовать запрос POST для настройки устройства.

Для одной команды rpc общий формат конечных точек:

схема://имя-устройства:порт/rpc/метод[@атрибуты]/параметры

  • схема 9 0042 : http или https

  • метод : Имя любой команды Junos OS rpc . Имя метода идентично элементу тега. Дополнительные сведения см. в разделе «Операции протокола XML Junos, инструкции по обработке и теги ответов» в Руководстве разработчика протокола управления XML Junos и в Справочнике по эксплуатации для разработчиков XML API Junos .

  • params : Необязательные значения параметров ( имя[=значение] ).

Для аутентификации запроса отправьте имя пользователя и пароль в кодировке base64, включенные в заголовок авторизации:

 curl -u "имя пользователя:пароль" http://имя-устройства:порт/rpc/get-interface-information 

Чтобы указать данные rpc в качестве строки запроса в URI для запросов POST, отправьте данные запроса в теле POST. В таких случаях вы можете указать Content-Type как text/plain или application/xml , как показано в этих эквивалентных вызовах cURL:

 curl -u "имя пользователя: пароль" http://имя-устройства :port/rpc/get-interface-information --header "Content-Type: text/plain" –d "interface-name=cbp0"
curl -u "имя пользователя: пароль" http://имя-устройства:порт/rpc/get-interface-information --header "Тип содержимого: application/xml" –d "cbp0"
 

Как для одной, так и для нескольких команд RPC заголовки HTTP Accept могут использоваться для указания формата возврата с использованием одного из следующих значений Content-Type:

Например, следующий вызов cURL указывает выходной формат JSON:

 curl -u "имя пользователя: пароль" http://имя-устройства:порт/rpc -d  --header "Accept: application/json" 

Вы также можете указать формат вывода, используя необязательный параметр формат атрибут:

 curl -u "имя пользователя:пароль" http://имя-устройства:порт/rpc -d "" 
Примечание:

Content-Type по умолчанию для Запросы POST, содержащие аргументы в теле, имеют вид application/xml. Если вы хотите использовать любой другой контент, например строку запроса, вы можете указать Content-Type text/plain. Укажите атрибут формата в командах конфигурации.

При выполнении нескольких команд rpc в одном запросе общий формат конечной точки:

схема://имя-устройства:порт/rpc

RPC должны предоставляться в виде данных XML в теле POST. Content-Type для ответа — составной/смешанный, с границей и подтипом, связанным с выходными данными каждого выполнения RPC. Формат, указанный в заголовке Accept, используется в качестве выходного формата для каждого RPC, если в них отсутствует атрибут формата . Если заголовок Accept не указан и в заданном RPC не указан атрибут формата , выходным форматом по умолчанию является XML. Например, чтобы отправить один HTTP-запрос для выполнения RPC get-software-information и get-interface-information , отправьте запрос POST на /rpc с «Auth: Basic » , «Content-Type: application/xml» . Тело POST будет содержать:

Вот вызов cURL с использованием этого тела POST:

 curl -u "имя пользователя: пароль" http: //имя-устройства:порт/rpc -d "" 

Вывод запроса, содержащего XML по умолчанию, будет выглядеть следующим образом:

 HTTP/1.1 200 OK
Content-Type: составной/смешанный; граница = fkj49sn38dcn3
Передача-кодирование: по частям
Дата: Чт, 20 марта 2014 г., 11:01:27 по Гринвичу
Сервер: lighttpd/1.4.32
--fkj49sn38dcn3
Тип содержимого: приложение/xml
<информация о программном обеспечении>
<имя-хоста>...
...

--fkj49sn38dcn3
Тип содержимого: приложение/xml
<информация об интерфейсе>
<физический-интерфейс>...

--fkj49sn38dcn3-- 

Вы также можете указать выходной формат для каждого из элементов в теле POST.