Home > database >  Cannot return String from @RestController method
Cannot return String from @RestController method

Time:12-22

I'm working with Spring Boot 2.4.5, MyBatis 3.5.6 and Java 8. When trying to return a String from a @RestController, an obscure error shows up in the returned HttpErrorResponse.

The method tries to obtain the value via MyBatis, i.e., a method in a DAO object that acts as a @Mapper.

My controller method:

@RestController
@RequestMapping("/api/myAPI")
public class MyController{
    @Resource
    private MyService service;

    @GetMapping(value = "myString")
    public String getBillingCompany() {
        return this.service.getDAO().getMyString();
    }
}

My DAO:

@Repository
@Mapper
public interface MyDAO{
    String getMyString();
}

...and the MyBatis mapper:

<mapper namespace="com.package.MyDAO">
    <select id="getMyString" resultType="String">
        SELECT 'My desired result' FROM A_TABLE
    </select>
    ...
</mapper>

The HttpErrorResponse:

HttpErrorResponse: {
    "headers": {
        "normalizedNames": {},
        "lazyUpdate": null
    },
    "status": 200,
    "statusText": "OK",
    "url": "http://localhost:4200/api/myAPI/myString",
    "ok": false,
    "name": "HttpErrorResponse",
    "message": "Http failure during parsing for http://localhost:4200/api/myAPI/myString",
    "error": {
        "error": {  SyntaxError: Unexpected number in JSON at position 2
                    at JSON.parse (<anonymous>)
                    at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:18158:51)
                    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:21266:35)
                    at Object.onInvokeTask (http://localhost:4200/vendor.js:74037:33)
                    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:21265:40)
                    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (http://localhost:4200/polyfills.js:21033:51)
                    at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:21348:38)
                    at invokeTask (http://localhost:4200/polyfills.js:22516:18)
                    at XMLHttpRequest.globalZoneAwareCallback (http://localhost:4200/polyfills.js:22553:25)
                 },
        "text": "My desired result"
    }
}

Nonetheless, if I ask the controller and the DAO methods to return an int, it all works flawlessly.

Due to this, I suspected that the issue has to do with non-primitive types "namespacing", so I've tried to set a typeAlias in the MyBatis configuration, to no avail:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias type="java.lang.String" alias="String"/>
    </typeAliases>
</configuration>

Anyways, I'm under the impression that both MyBatis and Spring should be already smart enough to know what a String is. I've successfully returned collections of objects (Maps and Lists) and POJOs in the past.

Any ideas on what I'm lacking or not seeing? Thanks in advance.

Edit: The only thing that has worked for me so far is similar to what @emeraldjava proposed. I've built a wrapper upon an existing one in a dependency, fetching the data in my Front:

@RestController
@RequestMapping("/api/myAPI")
public class MyController{
    @Resource
    private MyService service;

    @GetMapping(value = "myString")
    public Result<String> getBillingCompany() {
        return new Result<>(this.service.getDAO().getMyString());
    }
}
public class Result<T> extends ServiceResult {
    public Result(T data) {
        this.setData(data);
    }
}

The already existing wrapper in a dependency:

public class ServiceResult {
    private Object data;

    ...

    public void setData(Object data) {
        this.data = data;
    }

    public Object getData() {
        return this.data;
    }
}

CodePudding user response:

I think the problem is that the table you're querying has multiple rows, so the result of your query will be a List of string, containing "My desired result" on each element, in which the size of the list is equal to the number of rows in the table. In order to force a single result, just change the query to:

<select id="getMyString" resultType="String">
        SELECT MAX('My desired result') FROM A_TABLE
</select>

CodePudding user response:

I'd suggest you update your Controller method to a ResponseEntity which wraps the string.

@RestController
@RequestMapping("/api/myAPI")
public class MyController{
    @Resource
    private MyService service;

    @GetMapping(value = "myString")
    public ResponseEntity getBillingCompany() {
        return new ResponseEntity<Object>(this.service.getDAO().getMyString(), HttpStatus.OK);
    }
}
  • Related