Combate real SpringBoot: um truque para obter uma resposta elegante ao resultado

Combate real SpringBoot: um truque para obter uma resposta elegante ao resultado
Como as coisas estão muito complicadas recentemente, muitos artigos não têm continuidade. Sempre que você pensar nisso, todos, Haihan, irão esclarecer seus pensamentos mais tarde, e sairão de forma coerente e formarão uma série para você encontrar falhas.

Hoje, vamos falar sobre como o Spring Boot consegue uma resposta de dados elegante: formato de resposta de resultado unificado e encapsulamento de dados simples.

premissa

Independentemente do tamanho do sistema, a maioria dos projetos Spring Boot fornece uma interface Restful + json para o front-end ou outros serviços chamarem. O formato é unificado e padronizado. É um símbolo de que os programadores se tratam bem e também uma garantia básica para a redução de depuração e repreensão conjunta.

Normalmente, o resultado da resposta precisa incluir o código de status do negócio, a descrição da resposta, o carimbo de data / hora da resposta e o conteúdo da resposta, como:

{
    
    
  "code": 200,
  "desc": "查询成功",
  "timestamp": "2020-08-12 14:37:11",
  "data": {
    
    
    "uid": "1597242780874",
    "name": "测试 1"
  }
}

Existem duas facções para os códigos de status de serviço: uma é que os códigos de resposta HTTP são recomendados para serem retornados como serviços de interface; a outra é que todos os códigos de resposta HTTP retornam 200 e um campo separado é usado para indicar o status da resposta no corpo da resposta . Os dois métodos têm suas próprias vantagens e desvantagens.Eu pessoalmente recomendo o segundo método, porque muitos servidores web têm funções de processamento de interceptação para códigos de status HTTP e o número de códigos de status é priorizado, o que não é flexível o suficiente. Por exemplo, o retorno de 200 indica que a interface foi processada com sucesso e a resposta é normal.Agora, um código de status é necessário para indicar que a interface foi processada com sucesso e a resposta é normal, mas o status dos dados solicitados está incorreto. Você pode retornar 2001 para indicar.

Corpo de resposta personalizado

Definir um corpo de resposta de dados é a primeira etapa para retornar um formato de resposta unificado. Independentemente de a interface retornar normalmente ou ocorrer uma exceção, o formato da estrutura retornado ao chamador deve permanecer inalterado. Dê um exemplo:

@ApiModel
@Data
public class Response<T> {
    
    
    @ApiModelProperty(value = "返回码", example = "200")
    private Integer code;
    @ApiModelProperty(value = "返回码描述", example = "ok")
    private String desc;
    @ApiModelProperty(value = "响应时间戳", example = "2020-08-12 14:37:11")
    private Date timestamp = new Date();
    @ApiModelProperty(value = "返回结果")
    private T data;
}

Desta forma, desde que seja retornado no método Controller Response, a resposta da interface será consistente, mas isso formará muitos modelos de código com um formato fixo, como a seguinte escrita:

@RequestMapping("hello1")
public Response<String> hello1() {
    
    
    final Response<String> response = new Response<>();
    response.setCode(200);
    response.setDesc("返回成功");
    response.setData("Hello, World!");
    return response;
}

O resultado da resposta ao chamar a interface é:

{
    
    
  "code": 200,
  "desc": "返回成功",
  "timestamp": "2020-08-12 14:37:11",
  "data": "Hello, World!"
}

Como pode esse tipo de código repetitivo e não técnico ser digno de uma criatura excelente (lan) e elegante (dupla) como um programador? É melhor subtrair esses códigos repetidos com base na premissa de retornar o resultado da resposta, como:

@RequestMapping("hello2")
public String hello2() {
    
    
    return "Hello, World!";
}

Isso precisa ResponseBodyAdviceser alcançado com a ajuda do Spring .

Processar dados de resposta globalmente

Primeiro no código:

/**
 * <br>created at 2020/8/12
 *
 * @author www.howardliu.cn
 * @since 1.0.0
 */
@RestControllerAdvice
public class ResultResponseAdvice implements ResponseBodyAdvice<Object> {
    
    
    @Override
    public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
    
    
        return !returnType.getGenericParameterType().equals(Response.class);// 1
    }

    @Override
    public Object beforeBodyWrite(final Object body, final MethodParameter returnType, final MediaType selectedContentType,
                                  final Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  final ServerHttpRequest request, final ServerHttpResponse response) {
    
    
        if (body == null || body instanceof Response) {
    
    
            return body;
        }
        final Response<Object> result = new Response<>();
        result.setCode(200);
        result.setDesc("查询成功");
        result.setData(body);
        if (returnType.getGenericParameterType().equals(String.class)) {
    
    // 2
            ObjectMapper objectMapper = new ObjectMapper();
            try {
    
    
                return objectMapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {
    
    
                throw new RuntimeException("将 Response 对象序列化为 json 字符串时发生异常", e);
            }
        }
        return result;
    }
}

/**
 * <br>created at 2020/8/12
 *
 * @author www.howardliu.cn
 * @since 1.0.0
 */
@RestController
public class HelloWorldController {
    
    
    @RequestMapping("hello2")
    public String hello2() {
    
    
        return "Hello, World!";
    }

    @RequestMapping("user1")
    public User user1() {
    
    
        User u = new User();
        u.setUid(System.currentTimeMillis() + "");
        u.setName("测试1");
        return u;
    }
}

O código acima é ResponseBodyAdviceum método de modelo que implementa a classe Spring e pode ser implementado de acordo com os requisitos do Spring. Existem apenas dois locais que precisam de atenção especial, ou seja, os locais marcados com 1 e 2 no código.

Primeiro, que esta linha 1, que é o supportsmétodo, que é verificar se você precisa chamar beforeBodyWriteum método de pré-julgamento, retorne trueo beforeBodyWritemétodo de execução , onde o tipo de retorno do método de acordo com o Controlador para determinar se você precisa executar beforeBodyWrite, você sempre pode retornar true, para determinar se na parte de trás a conversão de tipo é necessária.

Em seguida, concentre-se na segunda linha. Esta linha é um poço, um grande poço. Se você não está familiarizado com a estrutura da mola, com certeza vai ficar aqui por muito tempo.

Código 2 Esta linha serve para determinar se Controllero método retorna o Stringresultado do tipo.Se for, o objeto retornado é serializado e retornado.

Isso se deve Springao Stringtipo de resposta ao tipo de tratamento sozinho, utilizando StringHttpMessageConvertera conversão de dados da classe. Ao processar o resultado da resposta, o getContentLengthtamanho do corpo da resposta é calculado no método . A definição do método pai é protected Long getContentLength(T t, @Nullable MediaType contentType), e StringHttpMessageConvertero método é reescrito como protected Long getContentLength(String str, @Nullable MediaType contentType), o primeiro parâmetro é o objeto de resposta e o Stringtipo fixo é o tipo. Se forçarmos o retorno Responseobjeto, irá relatar ClassCastException.

Claro, não há Stringmuitos cenários para retorno direto , e esse poço pode aparecer repentinamente em uma interface especial um dia.

Explicação complementar

O acima apenas mostra a ResponseBodyAdviceaplicação mais simples da classe, também podemos alcançar um uso mais extenso. tal como:

  1. ID do pedido de retorno: precisa ser RequestBodyAdvicevinculado e, depois que o ID do pedido é obtido, a resposta é colocada no corpo da resposta;

  2. Criptografia de dados de resultado: ao ResponseBodyAdviceimplementar a criptografia de dados de resposta, os códigos de negócios não serão invadidos e o nível de criptografia da interface pode ser tratado de forma flexível por meio de anotações;

  3. Opcionalmente, envolva o corpo da resposta: por exemplo, defina anotações IgnoreResponseWrap, defina na interface que não precisa envolver o corpo da resposta e, a seguir, supportsjulgue as anotações do método no método, como:

     @Override
     public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
          
          
         final IgnoreResponseWrap[] declaredAnnotationsByType = returnType.getExecutable().getDeclaredAnnotationsByType(IgnoreResponseWrap.class);
         return !(declaredAnnotationsByType.length > 0 || returnType.getGenericParameterType().equals(Response.class));
     }
    

Muitos outros jogos não são listados um por um.

Resumindo

Os dados de resposta normal mencionados acima são apenas um pouco elegantes. Se você quiser ser completo, também precisa considerar a situação anormal da interface. Você não pode chegar a um grande try/catch/finallypacote de lógica de negócios, é muito feio. Haverá outro artigo posteriormente, focando em como a interface pode retornar uma resposta de resultado unificada quando ocorre uma exceção.

Este artigo joga um tijolo fora, e Yu tem que encontrá-lo sozinho.


Página pessoal: https://www.howardliu.cn
Postagem do blog pessoal: SpringBoot real combat: um movimento para obter uma resposta elegante ao resultado da
página inicial do CSDN: http://blog.csdn.net/liuxinghao
Postagem do blog do CSDN: SpringBoot atual combate: um movimento para alcançar o resultado Resposta graciosa

Número público: Vigiando a cabana na montanha

Acho que você gosta

Origin blog.csdn.net/conansix/article/details/107970059
Recomendado
Clasificación