API接口幂等性框架设计

SMP接口与API开发:核心概念与实践指南 接口与API是现代软件开发中系统通信的基础组件。接口作为抽象契约定义行为规范,而API提供具体功能实现。在SMP开发体系中,接口通过`interface`关键字定义方法签名集合,API则暴露平台级能力如硬件操作。理解接口定义规范与API调用机制对构建稳定系统至关重要,尤其在需要严格类型检查和多继承支持的场景。通过分层设计的API架构(硬件抽象层、系统服务层等)和自动化测试框架,开发者能有效提升物联网设备控制、支付系统等关键业务的可靠性。本文以车载设备项目为例,展示如何通过版本控制实现接口平滑升级。 阅读详情

API接口幂等性框架设计

  表单重复提价问题

  rpc远程调用时候 发生网络延迟  可能有重试机制

  MQ消费者幂等(保证唯一)一样 

 

   解决方案: token

   令牌 保证唯一的并且是临时的  过一段时间失效

   分布式: redis+token

  

注意在getToken() 这种方法代码一定要上锁  保证只有一个线程执行  否则会造成token不唯一

 

步骤 调用接口之前生成对应的 token,存放在redis中

        调用接口的时候,将该令牌放到请求头中 (获取请求头中的令牌)

        接口获取对应的令牌,如果能够获取该令牌 (将当前令牌删除掉),执行该方法业务逻辑 

        如果获取不到对应的令牌。返回提示“老铁 不要重复提交”

 

哈哈 如果别人获得了你的token 然后拿去做坏事,采用机器模拟去攻击。这时候我们要用验证码来搞定。

 

 

从代码开发者的角度看,如果每次请求都要 获取token  然后进行一统校验。代码冗余啊。如果一百个接口 要写一百次

所以采用AOP的方式进行开发,通过注解方式。

如果过滤器的话,所有接口都进行了校验。

 

框架开发:

 自定义一个注解@  作为标记

 如果哪个Controller需要进行token的验证加上注解标记

 在执行代码时候AOP通过切面类中 写的 作用接口进行 判断,如果这个接口方法有 自定义的@注解  那么进行校验逻辑

 校验结果 要么提示给用户 “请勿提交” 要么通过验证 继续往下执行代码

关于表单重复提交: 

 在表单有个隐藏域 存放token  使用  getParameter 去获取token 然后通过返回的结果进行校验

 注意 获取token的这个代码 也是用AOP去解决,实现。 否则每个Controller类都写这段代码就冗余了。前置通知搞定

注解:

首先pom:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <dependencies>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- SpringBoot 对lombok 支持 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- SpringBoot web 核心组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <!-- SpringBoot 外部tomcat支持 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>

        <!-- springboot-log4j -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>
        <!-- springboot-aop 技术 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
    </dependencies>

 

 

2、关于表单提交的注解的封装

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtApiIdempotent {
    String value();
}

 

AOP:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.itmayeidu.ext.ExtApiIdempotent;
import com.itmayeidu.ext.ExtApiToken;
import com.itmayeidu.utils.ConstantUtils;
import com.itmayeidu.utils.RedisTokenUtils;
import com.itmayeidu.utils.TokenUtils;

@Aspect
@Component
public class ExtApiAopIdempotent {
    @Autowired
    private RedisTokenUtils redisTokenUtils;
   
    //需要作用的类
    @Pointcut("execution(public * com.itmayiedu.controller.*.*(..))")
    public void rlAop() {
    }

    // 前置通知转发Token参数  进行拦截的逻辑  
    @Before("rlAop()")
    public void before(JoinPoint point) {
        //获取并判断类上是否有注解   
        MethodSignature signature = (MethodSignature) point.getSignature();//统一的返回值
        ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class);//参数是注解的那个
        if (extApiToken != null) { //如果有注解的情况
            extApiToken();
        }
    }

    // 环绕通知验证参数
    @Around("rlAop()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        ExtApiIdempotent extApiIdempotent = signature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
        if (extApiIdempotent != null) { //有注解的情况 有注解的说明需要进行token校验   
            return extApiIdempotent(proceedingJoinPoint, signature);
        }
        // 放行
        Object proceed = proceedingJoinPoint.proceed(); //放行 正常执行后面(Controller)的业务逻辑
        return proceed;
    }

    // 验证Token  方法的封装
    public Object extApiIdempotent(ProceedingJoinPoint proceedingJoinPoint, MethodSignature signature)
            throws Throwable {
        ExtApiIdempotent extApiIdempotent = signature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
        if (extApiIdempotent == null) {
            // 直接执行程序
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        }
        // 代码步骤:
        // 1.获取令牌 存放在请求头中
        HttpServletRequest request = getRequest();
        // value就是获取类型 请求头之类的
        String valueType = extApiIdempotent.value();
        if (StringUtils.isEmpty(valueType)) {
            response("参数错误!");
            return null;
        }
        String token = null;
        if (valueType.equals(ConstantUtils.EXTAPIHEAD)) { //如果存在header中 从头中获取
            token = request.getHeader("token"); //从头中获取
        } else {
            token = request.getParameter("token"); //否则从 请求参数获取
        }
        if (StringUtils.isEmpty(token)) {
            response("参数错误!");
            return null;
        }
        if (!redisTokenUtils.findToken(token)) {
            response("请勿重复提交!");
            return null;
        }
        Object proceed = proceedingJoinPoint.proceed();
        return proceed;
    }

    public void extApiToken() {
        String token = redisTokenUtils.getToken();
        getRequest().setAttribute("token", token);

    }

    public HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        return request;
    }

    public void response(String msg) throws IOException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        try {
            writer.println(msg);
        } catch (Exception e) {

        } finally {
            writer.close();
        }

    }

}

订单请求接口:

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.itmayeidu.ext.ExtApiIdempotent;
import com.itmayeidu.utils.ConstantUtils;
import com.itmayeidu.utils.RedisTokenUtils;
import com.itmayeidu.utils.TokenUtils;
import com.itmayiedu.entity.OrderEntity;
import com.itmayiedu.mapper.OrderMapper;


@RestController
public class OrderController {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RedisTokenUtils redisTokenUtils;

    // 从redis中获取Token
    @RequestMapping("/redisToken")
    public String RedisToken() {
        return redisTokenUtils.getToken();
    }

    // 验证Token
    @RequestMapping(value = "/addOrderExtApiIdempotent", produces = "application/json; charset=utf-8")
    @ExtApiIdempotent(value = ConstantUtils.EXTAPIHEAD)
    public String addOrderExtApiIdempotent(@RequestBody OrderEntity orderEntity, HttpServletRequest request) {
        int result = orderMapper.addOrder(orderEntity);
        return result > 0 ? "添加成功" : "添加失败" + "";
    }
}

表单提交的请求接口:

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.itmayeidu.ext.ExtApiIdempotent;
import com.itmayeidu.ext.ExtApiToken;
import com.itmayeidu.utils.ConstantUtils;
import com.itmayiedu.entity.OrderEntity;
import com.itmayiedu.mapper.OrderMapper;

@Controller
public class OrderPageController {
    @Autowired
    private OrderMapper orderMapper;

    @RequestMapping("/indexPage")
    @ExtApiToken
    public String indexPage(HttpServletRequest req) {
        return "indexPage";
    }

    @RequestMapping("/addOrderPage")
    @ExtApiIdempotent(value = ConstantUtils.EXTAPIFROM)
    public String addOrder(OrderEntity orderEntity) {
        int addOrder = orderMapper.addOrder(orderEntity);
        return addOrder > 0 ? "success" : "fail";
    }

}

utils:

redis:

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class BaseRedisService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void setString(String key, Object data, Long timeout) {
        if (data instanceof String) {
            String value = (String) data;
            stringRedisTemplate.opsForValue().set(key, value);
        }
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    public Object getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public void delKey(String key) {
        stringRedisTemplate.delete(key);
    }

}

常量:

public interface ConstantUtils {

    static final String EXTAPIHEAD = "head";

    static final String EXTAPIFROM = "from";
}

mvc:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@ComponentScan("com.too5.controller")
public class MyMvcConfig {
    @Bean // 出现问题原因 @bean 忘记添加
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }

}

redis操作token工具类:

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RedisTokenUtils {
    private long timeout = 60 * 60;  //超时时间
    @Autowired
    private BaseRedisService baseRedisService;

    // 将token存入在redis
    public String getToken() {
        String token = "token" + System.currentTimeMillis();
        baseRedisService.setString(token, token, timeout);  //key: token value: token 时间
        return token;
    }

    public synchronize boolean findToken(String tokenKey) {  //从redis查询对应的token   防止没来得及删除 只有一个线程操作 其实redis已经可以防止了
        String token = (String) baseRedisService.getString(tokenKey);
        if (StringUtils.isEmpty(token)) { //要么被被人使用过了 要么没有对应token
            return false;
        }
        // token 获取成功后 删除对应tokenMapstoken
        baseRedisService.delKey(token);
        return true;  //保证每个接口对应的token只能访问一次,保证接口幂等性问题
    }

}

tokenutils:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;

public class TokenUtils {

    private static Map<String, Object> tokenMaps = new ConcurrentHashMap<String, Object>();
    // 1.什么Token(令牌) 表示是一个零时不允许有重复相同的值(临时且唯一)
    // 2.使用令牌方式防止Token重复提交。

    // 使用场景:在调用第API接口的时候,需要传递令牌,该Api接口 获取到令牌之后,执行当前业务逻辑,让后把当前的令牌删除掉。
    // 在调用第API接口的时候,需要传递令牌 建议15-2小时
    // 代码步骤:
    // 1.获取令牌
    // 2.判断令牌是否在缓存中有对应的数据
    // 3.如何缓存没有该令牌的话,直接报错(请勿重复提交)
    // 4.如何缓存有该令牌的话,直接执行该业务逻辑
    // 5.执行完业务逻辑之后,直接删除该令牌。

    // 获取令牌
    public static synchronized String getToken() {
        // 如何在分布式场景下使用分布式全局ID实现
        String token = "token" + System.currentTimeMillis();
        // hashMap好处可以附带
        tokenMaps.put(token, token);
        return token;
    }

    // generateToken();

    public static boolean findToken(String tokenKey) {
        // 判断该令牌是否在tokenMap 是否存在
        String token = (String) tokenMaps.get(tokenKey);
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        // token 获取成功后 删除对应tokenMapstoken
        tokenMaps.remove(token);
        return true;
    }
}

实体类:

public class OrderEntity {

    private int id;
    private String orderName;
    private String orderDes;

    
    public int getId() {
        return id;
    }


    public void setId(int id) {
        this.id = id;
    }

    
    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    
    public String getOrderDes() {
        return orderDes;
    }


    public void setOrderDes(String orderDes) {
        this.orderDes = orderDes;
    }

}
public class UserEntity {

    private Long id;
    private String userName;
    private String password;

  
    public Long getId() {
        return id;
    }

    
    public void setId(Long id) {
        this.id = id;
    }

  
    public String getUserName() {
        return userName;
    }

   
    public void setUserName(String userName) {
        this.userName = userName;
    }

    
    public String getPassword() {
        return password;
    }

    
    public void setPassword(String password) {
        this.password = password;
    }

  
    @Override
    public String toString() {
        return "UserEntity [id=" + id + ", userName=" + userName + ", password=" + password + "]";
    }

}

Mapper:

import org.apache.ibatis.annotations.Insert;

import com.itmayiedu.entity.OrderEntity;

public interface OrderMapper {
    @Insert("insert order_info values (null,#{orderName},#{orderDes})")
    public int addOrder(OrderEntity OrderEntity);
}
public interface UserMapper {

    @Select(" SELECT  * FROM user_info where userName=#{userName} and password=#{password}")
    public UserEntity login(UserEntity userEntity);

    @Insert("insert user_info values (null,#{userName},#{password})")
    public int insertUser(UserEntity userEntity);
}

yml:

spring:
  mvc:
    view:
      # 页面默认前缀目录
      prefix: /WEB-INF/jsp/
      # 响应页面默认后缀
      suffix: .jsp

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    test-while-idle: true
    test-on-borrow: true
    validation-query: SELECT 1 FROM DUAL
    time-between-eviction-runs-millis: 300000
    min-evictable-idle-time-millis: 1800000
  redis:
    database: 1
    host: 106.15.185.133
    port: 6379
    password: meitedu.+@
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 10000
domain: 
 name: www.toov5.com
 

启动类:

@MapperScan(basePackages = { "com.tov5.mapper" })
@SpringBootApplication
@ServletComponentScan
public class AppB {

    public static void main(String[] args) {
        SpringApplication.run(AppB.class, args);
    }

}

 

 

总结:

核心就是

自定义注解

controller中的方法注解

aop切面类判断对象是否有相应的注解 如果有 从parameter或者header获取参数 进行校验

接口与通讯知识地图:从RS485到API的分层排错与设计 接口与通讯是嵌入式、工控和软件开发中的基础概念,却常被混淆。接口是静态约定,通讯是动态过程,理解二者的区别是排障的关键。通过分层视角,从物理层的RS485、ST-Link到应用层的Modbus、PROFINET和HTTP API,可以构建完整知识体系。RS485现场布线的终端电阻与接地问题、Modbus寄存器模型与大小端解析、PROFINET与Modbus TCP的选型,以及接口设计中的幂等性和封装细节,都是实际工程中的高频痛点。掌握分层定位、逐步验证的排错思路,无论面对工业总线还是Web接口,都能快速定位 阅读详情

相关推荐

从FrSky到TBS:深度解析航模2.4G遥控协议江湖史(附ELRS高频头改装指南)

本文深度解析了航模2.4G遥控协议的技术演进史,从FrSky ACCST的兴衰到TBS Crossfire的崛起,再到开源协议ExpressLRS的革命性突破。文章不仅对比了各协议在延迟、距离与抗干扰能力上的技术内核,还提供了为多协议遥控器加装TBS Crossfire高频头的实战改装指南,帮助玩家打通协议壁垒,提升飞行体验。

weixin_29193259的博客 736

开发中是如何保证接口幂等性的?

开发中,幂等性问题常见的几种解决方案

weixin_38491073的博客 2459

Keil AC6编译器独立安装与配置指南:不升级Keil MDK也能用上新工具链

本文详细介绍了如何在不升级Keil MDK的情况下独立安装和配置Arm Compiler 6(AC6),帮助嵌入式开发者充分利用AC6的编译速度、代码密度和现代语言支持优势。通过零侵入式解决方案,开发者可以在现有环境中灵活切换编译器版本,提升开发效率。

weixin_30872157的博客 552

如何保证接口幂等性(10种方案必有一个合适你)

接口幂等性就是说用户使用相同的参数请求同一个接口无论是一次还是多次都应该是一样的。不会因为多次的点击产生不同效果。举个栗子:一个用户在手机APP上提200块钱,然后一不小心点击了两次,那么就应该只提取出200块钱,不应该出来400(当然,真实场景下取钱操作是一个复杂事务,不可能一个接口点击就出来了)。对于这种场景下,即使用户点了两次也应该只取出一次的钱,就是接口幂等性。

zpskt的博客 3825

STM32F103C8T6心电采集实战:从PCB设计到上位机开发的完整指南(附TI杯开源资料)

本文详细介绍了基于STM32F103C8T6的心电采集系统开发全流程,从PCB设计到上位机开发,特别适合大学生电子设计大赛参赛者参考。内容涵盖硬件设计精要、软件算法优化及上位机开发技巧,并附TI杯开源资料,帮助开发者低成本实现医疗级心电信号采集。

kafka6courier的博客 194

材料带隙预测(图神经网络)(⭐⭐)—— 用晶体图网络预测钙钛矿材料的能带隙,加速光伏材料筛选。技术栈:CGCNN、MEGNet、Materials Project数据集

钙钛矿太阳能电池以其高效率、低成本的优势迅速崛起,但传统“试错法”材料筛选效率极低,严重制约了光伏技术的迭代速度。近年来,基于图神经网络的材料带隙预测方法,特别是晶体图神经网络(CGCNN)和材料图网络(MEGNet),为这一问题提供了革命性的解决方案。本文系统梳理了材料带隙预测从传统计算到深度学习的演进历程,深入剖析了CGCNN与MEGNet的架构设计、数学原理及创新机制,详细阐述了Materials Project数据集的构建、特征工程与处理策略。

2201_76125393的博客 48

如何保证接口幂等性?一口气说了12种方法!

首先看看幂等性的概念:同一个接口,多次发出同一个请求,必须保证操作只执行一次。调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。比如下面这些情况,如果没有实现接口幂等性会有很严重的后果:支付接口,重复支付会导致多次扣钱;订单接口,同一个订单可能会多次创建。

m0_73311735的博客 2270

Android13无重力感应系统修改触摸屏跟随屏幕显示旋转方向旋转

在实际产品中设计时会移除重力传感器。当设备缺少重力传感器时,系统无法使用该传感器提供的方向数据来确定屏幕旋转的方向。Android 系统中由于移除重力传感器导致的默认显示方向旋转引起触摸屏与屏幕显示不一致问题,主要是由于缺乏方向检测和触摸坐标的正确转换。通过使用替代传感器(如陀螺仪)、手动配置方向、调整触摸事件坐标和 UI 布局,可以解决这个问题。开发者需要确保在设备没有重力传感器的情况下,能够通过其他方法推测方向,并确保触摸事件和 UI 动作能够正确响应设备的旋转。

weixin_35723192的博客 803

如何保证接口幂等性问题

如何保证接口幂等性问题

library_yb的博客 369

【软考中级】多媒体应用设计师复习笔记第十章【free】

第10章法律法规与标准规范 行业标准 缩写的大写拼音 地方标准 DB加生代码 企业标准Q加斜线企业代号国际标准是国际标准化组织ISO和国际电工委员会IEC制定的标准 国家标准是由国务院标准化行政主管部门制定,全国范围内使用 ?强制性国家标准的代号为GB,推荐性国家标准的代号为GB/T。 ?软件开发规范GB8566-88属于强制性国家标准 在这里插入图片描述 行业标准代号由大写汉语拼音字母组成,加上斜线和T,组成推荐性行业标准,如XX/T。已经发布的行业代号有QJ(航天),SJ(电子

weixin_47120900的博客 641

Leads、SDR、MQL、SQL、L2C术语概念,线索创建与清洗、线索分级模型、线索流转策略、线索的统计与分析

CRM中的“Leads”是什么?笔者在文中不仅较为全面地介绍了相关术语概念,还分析了线索的创建与清洗、线索的分级模型、线索流转策略以及线索的统计与分析。

盼盼学士 5960

如何保证接口的幂等性

如何保证幂等性

梦想家的跋涉 636

Flatten 层的深度理解与运用

Flatten 层的深度理解与运用

你要拥有一个你喜欢并且热爱的人生 7032

mysql的api框架_API接口幂等性框架设计

表单重复提价问题rpc远程调用时候 发生网络延迟 可能有重试机制MQ消费者幂等(保证唯一)一样解决方案: token令牌 保证唯一的并且是临时的 过一段时间失效分布式: redis+token注意在getToken() 这种方法代码一定要上锁 保证只有一个线程执行 否则会造成token不唯一步骤 调用接口之前生成对应的 token,存放在redis中调用接口的时候,将该令牌放到请求头中 (...

weixin_34738304的博客 201

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【PHP开发900个实用技巧】696.API幂等性设计:PHP保证请求重试安全的“定心丸”

文章摘要: 本文深入探讨PHP开发中的API幂等性设计,解决网络重试导致的重复请求问题。从基础认知到四大实战策略(Token令牌、唯一键约束、悲观锁、状态机),结合具体代码示例,帮助开发者避免支付失败/网络波动引发的业务风险。重点剖析常见陷阱如分布式事务超时和MQ消息重复消费,并给出Laravel/ThinkPHP框架的集成方案。通过幂等性设计,确保关键接口在多次调用时仅产生一次预期效果,提升系统健壮性。

944

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

概要设计模板 外部接口设计_0226 rest接口设计

背景为了更方便的书写和阐述问题,文章中按照第一人称的角度书写。作为一个以java为主要开发语言的工程师,我所描述的都是java相关的编码和设计。工程师的静态输出就是代码和文档,动态的就是各种应用程序(app,h5站点,微信公众号,小程序)。动态的先不讨论,主要讨论静态的。随意查看一个代码库,可以看到代码的编写过程,某些代码可能在现在看来实现很低效和可笑,但是在当时的技术和时间场景下,肯定是最优的输...

weixin_39870132的博客 1468

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

软件工程实践三:RESTful API 设计原则

本文介绍了REST API设计规范,旨在建立统一的接口风格,提升协作效率。规范涵盖资源建模、URI设计、HTTP方法、状态码、请求/响应格式等关键要素。核心原则包括资源导向、统一接口、无状态、可缓存和可演进。URI设计应使用复数名词表示资源,HTTP方法表达操作。规范提供了状态码使用指南,建议采用标准JSON格式请求/响应,包含统一的数据结构。文章以Todo列表为例,展示了完整的CRUD接口实现,包括GET/POST/PATCH/DELETE操作示例,并提供了Java/Spring控制器的实现代码。通过遵循

BXCQ_xuan的技术专栏 | 实战干货·实践教程·学习笔记 1246

0226 rest接口设计

align=left&display=inline&height=322&name=image.png&originHeight=322&originWidth=338&size=154389&status=done&style=none&width=338) 背景 为了更方便的书写和阐述问题,文章中按照第一人称的角度书写。作为一个以java为主要开发语言的工程师,我所描述的都是java相关的编...

游戏发行平台,跨境电商,低代码,物联网,数字人商业项目架构师 931

接口测试面试全攻略:从HTTP协议到自动化框架设计

接口测试是保障软件质量的关键环节,尤其在微服务与前后端分离架构中,它直接验证服务间通信的准确性与可靠性。其核心原理在于通过模拟客户端请求,验证服务端响应的状态码、数据结构与业务逻辑是否符合预期。掌握接口测试不仅能提升软件交付质量,还能通过自动化实现高效回归,是现代测试工程师的核心竞争力。在实际应用中,接口测试需覆盖功能、安全、性能等多维度,并借助如Postman、Pytest+Requests等工具构建可维护的自动化测试框架。本文聚焦接口测试面试高频考点,系统梳理从HTTP协议基础、测试用例设计到自动化框架

weixin_42619742的博客 323

归纳整理(持续更新)

整理

on_the_way_to_travel的专栏 182

如何处理项目幂等场景

处理幂等场景,通常指的是确保一个操作、一次请求或者一个函数被调用多次时,其结果仍然保持一致,即具有“幂等性”。○ 例如,在支付系统中,如果订单已经支付成功,那么再次提交支付请求时应该返回支付成功的信息,而不是重新处理支付。○ 在执行更新操作时,先读取当前资源的版本号或时间戳,然后将其与之前的值进行比较。○ 例如,在消息队列中,可以使用消息的唯一ID和幂等消费者来确保消息只被处理一次。○ 在执行操作之前,先检查资源的状态,以确定该操作是否应该被执行。○ 在设计API或接口时,应该尽量使接口具有幂等性。

weixin_39480141的博客 502

java web整点秒杀,Java高并发秒杀Api-web 层

章节目录前端交互设计RestfulSpring MVC理论整合配置MVC框架bootstrap+jquery福利彩蛋前端交互设计前端交互设计详情页流程逻辑Restful接口什么是RestfulRestful 是接口设计规范一种优雅的URI表达方式资源的状态和状态转移获取的是资源的状态,删除的时候,其实是更改资源的状态 put、post、deleteRestful示例image.pngimage.p...

weixin_39603217的博客 153

SpringBoot REST 接口

REST的理论基础中有一个重要的理念:统一接口; 统一接口主要有: 资源识别:对URL进行识别; 资源操作: HTTP verbs: GET、PUT、POST、DELETE 其中GET:请求获取,幂等; PUT:请求更新,幂等; POST:请求创建,非幂等; DELETE:请求删除,幂等; 字描述信息: Content-Type:响应、请求中均包含该描述信息,主要描述请求或响应的内容格...

baidu_41934937的博客 448

PHP为什么接口要设计成 RESTful的庖丁解牛

维度非 RESTful (RPC/Soap)RESTful核心概念动作 (Action/Function)资源 (Resource)URL 设计/users/1HTTP 方法大多用 POST/GET状态码永远 200,Body 里写 code精确使用 2xx, 4xx, 5xx缓存难缓存 (POST)易缓存 (GET + Headers)可扩展性紧耦合,改接口需改客户端松耦合,标准通用适用场景内部高性能微服务对外 API, 前后端分离, 开放平台终极心法。

长风破浪会有时的博客 548

Django的前后端分离以及Rest风格接口开发大全

1.什么是前后端分离开发: 就是前后端工程师约定好数据交互接口,并行的进行开发和测试,后端只提供数据,不负责将数据渲染到页面上,前端通过HTTP请求获取数据并负责将数据渲染到页面上,这个工作是交给浏览器中的JavaScript代码来完成。 2.前后端开发的好处: 1.提升开发效率 2.增强代码的可维护性。 3支持多终端和服务化架 3.数据接口: FBV - 基于函数的视图 CBV - 基于类的视图 FBV 前后端分离源码 需要判断请求方式 序列化,反序列化难度大 CBV django的前后端分

Jin1Yang的博客 4027
上一篇: 防盗链&CSRF&API接口幂等性设计
下一篇: 互联网开放平台API安全设计
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值