spring kotlin_使用GraphQL,Spring Boot和Kotlin进行错误处理

云和硕
2023-12-01

spring kotlin

问题 (Problem)

Error handling in GraphQL can be pretty tricky, as a response will always have HTTP status 200 OK. If a request fails, the JSON payload response will contain a root field called errors that contains the details of the failure.

GraphQL中的错误处理可能非常棘手,因为响应始终具有HTTP状态200 OK 。 如果请求失败,则JSON有效负载响应将包含一个名为errors的根字段,其中包含失败的详细信息。

For a query like the one below …

对于以下查询...

query{
  getCar(input: "B Class"){
    name
  }
}

… without proper error handling, the response will be something like this:

……如果没有适当的错误处理,响应将是这样的:

{
  "errors": [
    {
      "message": "Internal Server Error(s) while executing query"
    }
  ],
  "data": null
}

This isn’t helpful at all — the response doesn’t tell you what actually went wrong.

这根本没有帮助-响应不会告诉您实际出了什么问题。

(Solution)

1.设置 (1. The setup)

Let’s set up a Spring Boot project with GraphQL. Also, the code to this is available here.

让我们用GraphQL设置一个Spring Boot项目。 此外, 此处的代码也可用。

Go to https://start.spring.io/.

转到https://start.spring.io/

Select a Gradle project, and set the language as Kotlin.

选择一个Gradle项目,并将语言设置为Kotlin

You don’t have to select any dependencies for a bare-minimum implementation.

您不必为最低限度的实现选择任何依赖项。

Download the ZIP file, and open it in your favorite IDE.

下载ZIP文件,然后在您喜欢的IDE中打开它。

Jump to build.gradle.kts, and add:

跳转到build.gradle.kts ,并添加:

implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:6.0.1")
implementation("com.graphql-java-kickstart:graphql-java-tools:5.7.1")
runtimeOnly("com.graphql-java-kickstart:altair-spring-boot-starter:6.0.1")
runtimeOnly("com.graphql-java-kickstart:graphiql-spring-boot-starter:6.0.1")
runtimeOnly("com.graphql-java-kickstart:voyager-spring-boot-starter:6.0.1")
runtimeOnly("com.graphql-java-kickstart:playground-spring-boot-starter:6.0.1")

These are the GraphQL libraries you’d need to get started with your first endpoint.

这些是您的第一个端点开始需要的GraphQL库。

2.没有错误处理的端点 (2. Endpoint without error handling)

Let’s create two endpoints to create and fetch a resource.

让我们创建两个端点来创建和获取资源。

type Query{
    hello: String
    getCar(input: String) : Car!
}


type Mutation{
    createCar(input: CarInput): Car
}


type Car {
    name: String
    price: Int
    engineType: String
}


input CarInput{
    name: String!
    price: Int!
    engineType: String!
}

Add Service to provide the creation and fetching of car.

添加Service以提供car的创建和获取。

@Service
class CarService {
    fun getCar(input: String): Car {
        return CarMap.cars[input]!!
    }


    fun createCar(input: CarInput): Car {
        val car = Car(input.name, input.price, input.engineType)
        CarMap.cars[input.name] = car
        return car
    }
}


object CarMap {
    val cars = HashMap<String, Car>()
}

We have created a function to create a car and another to get back the car with the name as input. But in case the car isn’t found, this will throw an exception, and that gets translated to the response shown:

我们创建了一个function来创建car并创建了另一个functionname作为输入取回car 。 但是如果找不到汽车,它将抛出异常,并将其转换为所示的响应:

{
  "errors": [
    {
      "message": "Internal Server Error(s) while executing query"
    }
  ],
  "data": null
}

3.实施错误处理 (3. Implement error handling)

To be able to provide something sensible in case of an error or exception, we’ll add some more code.

为了能够在发生错误或异常时提供合理的信息,我们将添加更多代码。

open class CarNotFoundException(
        errorMessage: String? = "",
        private val parameters: Map<String, Any>? = mutableMapOf()
) : GraphQLException(errorMessage) {
    override val message: String?
        get() = super.message


    override fun getExtensions(): MutableMap<String, Any> {
        return mutableMapOf("parameters" to (parameters ?: mutableMapOf()))
    }


    override fun getErrorType(): ErrorClassification {
        return ErrorType.DataFetchingException
    }
}

Let’s understand what’s going on here.

让我们了解这里发生了什么。

  1. There’s a message field, which is self-explanatory.

    有一个消息字段,它是不言自明的。
  2. The overridden function, getExtensions(), is to provide a map of parameters. This could be different properties like ErrorCodes that you want to pass for an error.

    重写的函数getExtensions()用于提供参数映射。 这可能是您要为错误传递的不同属性,例如ErrorCodes

  3. The overridden function, getErrorTypes(), is to provide a classification of the error — for example, DataFetchingException or ValidationException.

    覆盖的函数getErrorTypes()用于提供错误的分类-例如, DataFetchingExceptionValidationException

In the above exception, you can see it extends from GraphQLException, which is the bridge between our custom CarNotFoundException and GraphQLError.

在上述exception ,您可以看到它是从GraphQLException扩展的,它是我们的自定义CarNotFoundExceptionGraphQLError之间的桥梁。

Now we’re going to add GraphQLException.

现在,我们将添加GraphQLException

When we add that, we need to make sure we create a separate Java module and add that GraphQLException in the Java module.

添加时,我们需要确保创建一个单独的Java模块,并将该GraphQLException添加到Java模块中。

Why?

为什么?

Because a Kotlin class can’t extend RuntimeException and implement GraphQLError at the same time. The reason for that can be found here.

因为Kotlin类无法扩展RuntimeException并同时实现GraphQLError 。 原因可以在这里找到。

So now we create a GraphQLException class.

因此,现在我们创建一个GraphQLException类。

public class GraphQLException extends RuntimeException implements GraphQLError {


    String customMessage;


    public GraphQLException(String customMessage) {
        this.customMessage = customMessage;
    }


    @Override
    public String getMessage() {
        return customMessage;
    }


    @Override
    public List<SourceLocation> getLocations() {
        return null;
    }


    @Override
    public ErrorClassification getErrorType() {
        return null;
    }
}

And from the CarService, we throw the exception when the car isn’t found.

CarService ,当找不到car时,我们抛出异常。

@Service
class CarService {
    fun getCar(input: String): Car {
        return CarMap.cars[input]
                ?: throw CarNotFoundException("Car with name = $input not found",
                        mapOf("name" to input))
    }


    fun createCar(input: CarInput): Car {
        val car = Car(input.name, input.price, input.engineType)
        CarMap.cars[input.name] = car
        return car
    }
}

With that change, the response will contain a lot more detail than before.

有了这一更改,响应将包含比以前更多的细节。

{
  "errors": [
    {
      "message": 
      "Exception while fetching data (/getCar) : Car with name = B Class not found",
      
      "path": [
        "getCar"
      ],
      "extensions": {
        "parameters": {
          "name": "B Class"
        },
        "classification": "DataFetchingException"
      }
    }
  ],
  "data": null
}

结论 (Conclusion)

You can find the full implementation here.

您可以在此处找到完整的实现。

I’m glad you made it to the end. It means you’re interested in GraphQL. Here’s another article in which I share my experience of using GraphQL for six months.

我很高兴您能做到这一点。 这意味着您对GraphQL感兴趣。 这是另一篇文章 ,我分享了六个月使用GraphQL的经验。

翻译自: https://medium.com/better-programming/error-handling-with-graphql-spring-boot-and-kotlin-ed55f9da4221

spring kotlin

 类似资料: