当前位置: 首页 > news >正文

Java使用原生HttpURLConnection实现发送HTTP请求

 Java 实现发送 HTTP 请求,系列文章:

《Java使用原生HttpURLConnection实现发送HTTP请求》

《Java使用HttpClient5实现发送HTTP请求》

《SpringBoot使用RestTemplate实现发送HTTP请求》

1、HttpURLConnection 类的介绍

HttpURLConnection 是 Java 提供的原生标准的用于发送 HTTP 请求和接收 HTTP 响应的一个类,它位于 java.net 包下,并继承了 URLConnection 类。

HttpURLconnection 是基于 HTTP 协议的,支持 get,post,put,delete 等各种请求方式,最常用的就是 get 和 post。

URLConnection 提供了一组方法来建立与 URL 之间的连接、发送请求和接收响应。

以下是 HttpURLConnection 类常用的方法:

方法说明
openConnection()用于打开与 URL 的连接,返回一个 URLConnection 对象。
setRequestMethod(String method)设置请求方法,如 GET、POST 等。
setRequestProperty(String key, String value)设置请求属性,如请求头参数。
getRequestMethod()获取当前请求的方法。
getRequestProperty(String key)获取指定请求属性的值。
connect()建立与URL的连接。
getInputStream()获取输入流,用于接收响应数据。
getOutputStream()获取输出流,用于发送请求数据。
getResponseCode()获取响应的状态码。
getHeaderField(String name)获取指定响应头字段的值。
setDoInput(boolean doinput)设置是否从 URLConnection 读入,默认为true。
setDoOutput(boolean dooutput)设置是否向 URLConnection 输出,默认为false。
setInstanceFollowRedirects(boolean followRedirects)设置是否自动执行重定向,默认为true。
disconnect()断开与URL的连接。

2、创建 HttpURLConnection 工具类

通过将常用的方法封装到工具类中,可以避免重复编写相同的代码,从而提高代码的复用性‌。

基于 HttpURLConnection 的 HTTP 请求工具类:

package com.pjb.consumer.util;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;/*** 基于 HttpURLConnection 的 HTTP 请求工具类* @author pan_junbiao**/
public class HttpURLConnectionUtil
{// 超时时间private final static int timeOut = 60000; //60秒/*** 发送 GET 请求并获取响应数据** @param url    请求地址* @param params 请求参数* @return 响应数据字符串*/public static String doGet(String url, Map<String, String> params){HttpURLConnection connection = null;BufferedReader reader = null;try{// 1、拼接 URLStringBuffer stringBuffer = new StringBuffer(url);if (params != null && !params.isEmpty()){stringBuffer.append("?");for (Map.Entry<String, String> entry : params.entrySet()){stringBuffer.append(entry.getKey()).append("=").append(entry.getValue()).append("&");}stringBuffer.deleteCharAt(stringBuffer.length() - 1);}URL targetUrl = new URL(stringBuffer.toString());// 2、建立链接connection = (HttpURLConnection) targetUrl.openConnection();// 设置请求方法为 GETconnection.setRequestMethod("GET");// 设置连接超时connection.setConnectTimeout(timeOut);// 设置读取响应超时connection.setReadTimeout(timeOut);// 3、获取响应结果StringBuilder response = new StringBuilder();reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = reader.readLine()) != null){response.append(line);}return response.toString();} catch (IOException e){e.printStackTrace();} finally{//释放资源releaseResource(connection, null, reader);}return null;}/*** 发送 POST 请求并获取响应数据** @param url    请求地址* @param params 请求参数* @return 响应数据字符串*/public static String doPost(String url, Map<String, String> params){HttpURLConnection connection = null;OutputStream outputStream = null;BufferedReader reader = null;try{// 1、创建 URL 对象URL targetUrl = new URL(url);// 2、建立链接connection = (HttpURLConnection) targetUrl.openConnection();// 设置请求方法为 POSTconnection.setRequestMethod("POST");// 设置连接超时connection.setConnectTimeout(timeOut);// 设置读取响应超时connection.setReadTimeout(timeOut);// 设置请求头部为默认:URL编码表单数据格式connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");// 允许写入输出流connection.setDoOutput(true);// 禁用缓存connection.setUseCaches(false);// 3、写入请求体outputStream = connection.getOutputStream();StringBuffer payload = new StringBuffer();if (params != null && !params.isEmpty()){for (Map.Entry<String, String> entry : params.entrySet()){payload.append(entry.getKey()).append("=").append(entry.getValue()).append("&");}payload.deleteCharAt(payload.length() - 1);}outputStream.write(payload.toString().getBytes());outputStream.flush();outputStream.close();// 4、获取响应结果StringBuilder response = new StringBuilder();reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;// 读取响应数据while ((line = reader.readLine()) != null){response.append(line);}return response.toString();} catch (IOException e){e.printStackTrace();} finally{//释放资源releaseResource(connection, outputStream, reader);}return null;}/*** 发送 JSON 格式的 POST 请求并获取响应数据** @param url       请求地址* @param jsonParam JSON格式的请求参数* @return 响应数据字符串*/public static String doJsonPost(String url, String jsonParam){HttpURLConnection connection = null;OutputStream outputStream = null;BufferedReader reader = null;try{// 1、创建 URL 对象URL targetUrl = new URL(url);// 2、建立链接connection = (HttpURLConnection) targetUrl.openConnection();// 设置请求方法为 POSTconnection.setRequestMethod("POST");// 设置请求头部为 JSON 格式connection.setRequestProperty("Content-Type", "application/json");// 设置连接超时connection.setConnectTimeout(timeOut);// 设置读取响应超时connection.setReadTimeout(timeOut);// 允许向服务器发送数据connection.setDoOutput(true);// 3、向服务器发送 JSON 数据outputStream = connection.getOutputStream();outputStream.write(jsonParam.getBytes());outputStream.flush();// 4、获取响应结果StringBuffer response = new StringBuffer();int responseCode = connection.getResponseCode();reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = reader.readLine()) != null){response.append(line);}return response.toString();} catch (Exception e){e.printStackTrace();} finally{//释放资源releaseResource(connection, outputStream, reader);}return null;}/*** 释放资源*/private static void releaseResource(HttpURLConnection connection, OutputStream outputStream, BufferedReader reader){if (connection != null){try{connection.disconnect();} catch (Exception e){System.out.println("连接关闭失败");}}if (outputStream != null){try{outputStream.close();} catch (IOException e){System.out.println("输出流关闭失败");}}if (reader != null){try{reader.close();} catch (IOException e){System.out.println("输入流关闭失败");}}}
}

3、综合实例

【实例】实现用户信息的查询、新增、修改、删除接口,并使用 HttpURLConnection 实现接口的请求。

(1)在 controller 层,创建用户信息控制器类,实现查询、新增、修改、删除接口。

package com.pjb.business.controller;import com.pjb.business.entity.UserInfo;
import com.pjb.business.exception.ApiResponseException;
import com.pjb.business.model.ApiModel.ApiResponseCode;
import com.pjb.business.model.ApiModel.ApiResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** 用户信息控制器类* @author pan_junbiao**/
@RestController
@RequestMapping("/user")
@Api(description = "用户信息控制器")
public class UserController
{/*** 查询用户信息*/@ApiOperation(value = "查询用户信息")@RequestMapping(value = "/getUserInfo", method = RequestMethod.GET)public ApiResponseResult<UserInfo> getUserInfo(Long userId){if (userId <= 0){//使用:全局异常处理throw new ApiResponseException(ApiResponseCode.PARAMETER_ERROR);}UserInfo userInfo = new UserInfo();userInfo.setUserId(userId);userInfo.setUserName("pan_junbiao的博客");userInfo.setBlogName("您好,欢迎访问 pan_junbiao的博客");userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");//使用:统一返回值return new ApiResponseResult(ApiResponseCode.SUCCESS, userInfo);}/*** 新增用户信息*/@ApiOperation(value = "新增用户信息")@RequestMapping(value = "/addUserInfo", method = RequestMethod.POST)public ApiResponseResult<Boolean> addUserInfo(@RequestBody UserInfo userInfo){if (userInfo == null || userInfo.getUserName() == null || userInfo.getUserName().length() == 0){//使用:全局异常处理throw new ApiResponseException(ApiResponseCode.PARAMETER_ERROR);}//使用:统一返回值return new ApiResponseResult(ApiResponseCode.SUCCESS, true);}/*** 修改用户信息*/@ApiOperation(value = "修改用户信息")@RequestMapping(value = "/updateUserInfo", method = RequestMethod.POST)public ApiResponseResult<Boolean> updateUserInfo(@RequestBody UserInfo userInfo){if (userInfo == null && userInfo.getUserId() <= 0){//使用:全局异常处理throw new ApiResponseException(ApiResponseCode.PARAMETER_ERROR);}//使用:统一返回值return new ApiResponseResult(ApiResponseCode.SUCCESS, true);}/*** 删除用户信息*/@ApiOperation(value = "删除用户信息")@RequestMapping(value = "/deleteUserInfo", method = RequestMethod.POST)public ApiResponseResult<Boolean> deleteUserInfo(Long userId,String userName){if (userId <= 0){//使用:全局异常处理throw new ApiResponseException(ApiResponseCode.PARAMETER_ERROR);}//使用:统一返回值return new ApiResponseResult(ApiResponseCode.SUCCESS, true);}
}

(2)使用 HttpURLConnection 发送 Get 请求,查询用户信息。

/*** 使用 HttpURLConnection 发送 Get 请求,查询用户信息*/
@Test
public void getUserInfo()
{//请求地址String url = "http://localhost:8085/user/getUserInfo";//请求参数Map<String, String> params = new HashMap<>();params.put("userId", "1");//发送 HTTP 的 Get 请求(核心代码)String httpResult = HttpURLConnectionUtil.doGet(url, params);//反序列化JSON结果ApiResponseResult<UserInfo> responseResult = JacksonUtil.getJsonToGenericityBean(httpResult, ApiResponseResult.class, UserInfo.class);UserInfo userInfo = responseResult.getData();System.out.println("响应JSON结果:" + httpResult);System.out.println("响应结果编码:" + responseResult.getCode());System.out.println("响应结果信息:" + responseResult.getMessage());System.out.println("用户编号:" + userInfo.getUserId());System.out.println("用户名称:" + userInfo.getUserName());System.out.println("博客信息:" + userInfo.getBlogName());System.out.println("博客地址:" + userInfo.getBlogUrl());
}

执行结果:

(3)使用 HttpURLConnection 发送 JSON 格式的 POST 请求,新增用户信息。

/*** 使用 HttpURLConnection 发送 JSON 格式的 POST 请求,新增用户信息*/
@Test
public void addUserInfo()
{//请求地址String url = "http://localhost:8085/user/addUserInfo";//请求参数UserInfo userInfo = new UserInfo();userInfo.setUserId(2L);userInfo.setUserName("pan_junbiao的博客");userInfo.setBlogName("您好,欢迎访问 pan_junbiao的博客");userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");String json = JacksonUtil.getBeanToJson(userInfo);//发送 JSON 格式的 POST 请求(核心代码)String httpResult = HttpURLConnectionUtil.doJsonPost(url, json);System.out.println("响应结果:" + httpResult);
}

执行结果:

响应结果:{"code":200000,"message":"操作成功","data":true}

(4)使用 HttpURLConnection 发送 POST 请求,删除用户信息。

/*** 使用 HttpURLConnection 发送 POST 请求,删除用户信息*/
@Test
public void deleteUserInfo()
{//请求地址String url = "http://localhost:8085/user/deleteUserInfo";//请求参数Map<String, String> params = new HashMap<>();params.put("userId","3");//发送 HTTP 的 POST 请求(核心代码)String httpResult = HttpURLConnectionUtil.doPost(url, params);System.out.println("响应结果:" + httpResult);
}

执行结果:

响应结果:{"code":200000,"message":"操作成功","data":true}

相关文章:

Java使用原生HttpURLConnection实现发送HTTP请求

Java 实现发送 HTTP 请求&#xff0c;系列文章&#xff1a; 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、HttpURLConnection 类的介绍 HttpURLConnection 是 Java 提供的…...

TinyC编译器5—词法分析

1.词法分析的概念 词法分析也称为 分词 &#xff0c;此阶段编译器从左向右扫描源文件&#xff0c;将其字符流分割成一个个的 词 &#xff08; token 、 记号 &#xff0c;后文中将称为 token &#xff09;。所谓 token &#xff0c;就是源文件中不可再进一步分割的一串字符&am…...

电子电气架构---智能计算架构和SOA应用

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…...

Python Numpy 实现神经网络自动训练:反向传播与激活函数的应用详解

Python Numpy 实现神经网络自动训练&#xff1a;反向传播与激活函数的应用详解 这篇文章介绍了如何使用 Python 的 Numpy 库来实现神经网络的自动训练&#xff0c;重点展示了反向传播算法和激活函数的应用。反向传播是神经网络训练的核心&#xff0c;能够通过计算梯度来优化模…...

Apache Calcite - 基于规则的查询优化

基于规则的查询优化 基于规则的查询优化&#xff08;Rule-based Query Optimization&#xff09;是一种通过应用一系列预定义的规则来优化查询计划的技术。这些规则描述了如何转换关系表达式&#xff0c;以提高查询执行的效率。基于规则的优化器并不依赖于统计信息&#xff0c…...

react学习笔记,ReactDOM,react-router-dom

react 学习 1. 下载与安装 下载 npm install -g create-react-app 安装 npx create-react-app xxx 推荐 npm init react-app xxx yarn create react-app xxx 2. 创建 react 元素 indexjs 文件 import React from "react"; import ReactDOM from "react…...

优化UVM环境(八)-整理project_common_pkg文件

书接上回&#xff1a; 优化UVM环境&#xff08;七&#xff09;-整理环境&#xff0c;把scoreboard拿出来放在project_common环境里 Prj_cmn_pkg.sv考虑到是后续所有文件的基础&#xff0c;需要引入uvm_pkg并把自身这个pkg import给后续的文件&#xff1a; 这里有3个注意事项&…...

【实战案例】Django框架连接并操作数据库MySQL相关API

本文相关操作基于上次操作基本请求及响应基础之上【实战案例】Django框架基础之上编写第一个Django应用之基本请求和响应 Django框架中默认会连接SQLite数据库&#xff0c;好处是方便无需远程连接&#xff0c;打包项目挪到其他环境安装一下依赖一会就跑起来&#xff0c;但是缺点…...

【其他】无法启动phptudy服务,提示错误2:系统找不到指定的文件

在服务中启动phpstudy服务时&#xff0c;提示“windows 无法启动phpstudy服务 服务(位于本地计算机上) 错误2:系统找不到指定的文件”的错误。导致错误的原因是可执行文件的路径不对&#xff0c;修改成正确的路径就可以了。 下面是错误的路径&#xff0c;会弹出错误窗口&#…...

AI驱动的支持截图或线框图快速生成网页应用的开源项目

Napkins.dev是什么 Napkins.dev是一个创新的开源项目&#xff0c;基于AI技术将用户的截图或线框图快速转换成可运行的网页应用程序。项目背后依托于Meta的Llama 3.1 405B大型语言模型和Llama 3.2 Vision视觉模型&#xff0c;结合Together.ai的推理服务&#xff0c;实现从视觉设…...

es集群索引是黄色

排查 GET /_cat/shards?hindex,shard,prirep,state,unassigned.reason 查询原因 发现node正常 执行重新分配 retry_failedtrue 参数告诉Elasticsearch重试那些因某种原因&#xff08;如节点故障、资源不足等&#xff09;而失败的分片分配。这个选项通常用来尝试再次分配那些…...

获取淘宝商品评论的方法分享-调用API接口item_review

在电商领域&#xff0c;商品评论是消费者了解产品、做出购买决策的重要依据。淘宝作为中国最大的电商平台之一&#xff0c;其商品评论系统涵盖了海量的用户反馈数据。为了帮助企业、电商数据分析师、市场研究人员以及普通消费者更高效地获取这些评论数据&#xff0c;淘宝开放平…...

MATLAB人脸考勤系统

MATLAB人脸考勤系统课题介绍 该课题为基于MATLAB平台的人脸识别系统。传统的人脸识别都是直接人头的比对&#xff0c;现实意义不大&#xff0c;没有一定的新意。该课题识别原理为&#xff1a;先采集待识别人员的人脸&#xff0c;进行训练&#xff0c;得到人脸特征值。测试的时…...

Spring篇(事务篇 - 基础介绍)

目录 一、JdbcTemplate&#xff08;持久化技术&#xff09; 1. 简介 2. 准备工作 2.1. 引入依赖坐标 2.2. 创建jdbc.properties 2.3. 配置Spring的配置文件 3. 测试 3.1. 在测试类装配 JdbcTemplate 3.2. 测试增删改功能 查询一条数据为实体类对象 查询多条数据为一个…...

qt EventFilter用途详解

一、概述 EventFilter是QObject类的一个事件过滤器&#xff0c;当使用installEventFilter方法为某个对象安装事件过滤器时&#xff0c;该对象的eventFilter函数就会被调用。通过重写eventFilter方法&#xff0c;开发者可以在事件处理过程中进行拦截和处理&#xff0c;实现对事…...

[ 钓鱼实战系列-基础篇-6 ] 一篇文章让你了解邮件服务器机制(SMTP/POP/IMAP)-1

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…...

wordpress伪静态规则

WordPress 伪静态规则是指将 WordPress 生成的动态 URL 转换为静态 URL 的规则&#xff0c;这样做可以提高网站的搜索引擎优化(SEO)效果&#xff0c;并且使得 URL 更加美观、易于记忆。伪静态规则通常需要在服务器的配置文件中设置&#xff0c;不同的服务器环境配置方法有所不同…...

缓存框架JetCache源码解析-缓存定时刷新

作为一个缓存框架&#xff0c;JetCache支持多级缓存&#xff0c;也就是本地缓存和远程缓存&#xff0c;但是不管是使用着两者中的哪一个或者两者都进行使用&#xff0c;缓存的实时性一直都是我们需要考虑的问题&#xff0c;通常我们为了尽可能地保证缓存的实时性&#xff0c;都…...

docker配置mysql8报错 ERROR 2002 (HY000)

通过docker启动的mysql&#xff0c;发现navicat无法连接&#xff0c;后来进入容器内部也是无法连接&#xff0c;产生以下错误 root9f3b90339a14:/var/run/mysqld# mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket …...

【Linux】为什么环境变量具有全局性?共享?写时拷贝优化?

环境变量表具有全局性的原因&#xff1a; 环境变量表之所以具有全局性的特征&#xff0c;主要是因为它们是在进程上下文中维护的&#xff0c;并且在大多数操作系统中&#xff0c;当一个进程创建另一个进程&#xff08;即父进程创建子进程&#xff09;时&#xff0c;子进程会继承…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...